/*
 * Visitor.cpp
 *
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Visitor.hpp"
#include "analex.h"
#include "ErrorManager.h"
#include "irtree.hpp"
#include "SymbolTable.hpp"
#include "AST.hpp"
#include <typeinfo>


static IDSymbolTable IDTable;

/********************************************************************
 *              Implementação da Classe VisitorASTprint              *
 ********************************************************************/
VisitorASTprint::VisitorASTprint() {
    tab = 0;
}

void VisitorASTprint::putTab() {
    fprintf(stdout, "\n");
    for (unsigned int i = 0; i < this->tab; i++) fprintf(stdout, "|    ");
}

int VisitorASTprint::visit(node_Program *p) {
    if (!p) return 0;
    fprintf(stdout, "\n\n+==========================================================================+\n");
    fprintf(stdout, "|                         ARVORE DE SINTAXE ABSTRATA                       |\n");
    fprintf(stdout, "+==========================================================================+\n\n");
    fprintf(stdout, "node_Program");
    tab++;
    if (p->classList) p->classList->acceptVisitor(this);
    fprintf(stdout, "\n\n+==================== AST CONSTRUIDA COM SUCESSO! =========================+\n\n");
    return 0;
}

int VisitorASTprint::visit(node_ClassList *cl) {
    putTab();
    fprintf(stdout, "Class Name: %s", cl->className);
    tab++;
    if (cl->extendedClassName) {
        putTab();
        fprintf(stdout, "Extended Class Name: %s", cl->extendedClassName);
    }
    if (cl->firstDecl) cl->firstDecl->acceptVisitor(this);
    tab--;
    if (cl->nextClass) cl->nextClass->acceptVisitor(this);
    return 0;
}

int VisitorASTprint::visit(node_VarList *vl) {
    if (vl->VarName) vl->VarName->acceptVisitor(this);
    if (vl->nextDecl) vl->nextDecl->acceptVisitor(this);
    return 0;
}

int VisitorASTprint::visit(node_MethodList *ml) {
    if (ml->methodName) {
        putTab();
        fprintf(stdout, "Method Name: %s", ml->methodName);
    }
    tab++;
    if (ml->returnType) {
        putTab();
        fprintf(stdout, "Return Type");
        tab++;
        ml->returnType->acceptVisitor(this);
        tab--;
    }
    if (ml->paramList) {
        putTab();
        fprintf(stdout, "Parameters List");
        tab++;
        ml->paramList->acceptVisitor(this);
        tab--;
    }
    if (ml->localVarList) {
        putTab();
        fprintf(stdout, "Local Variables");
        tab++;
        ml->localVarList->acceptVisitor(this);
        tab--;
    }
    if (ml->stmtList) {
        putTab();
        fprintf(stdout, "Statement Block");
        tab++;
        ml->stmtList->acceptVisitor(this);
        tab--;
    }
    tab--;
    if (ml->nextDecl) ml->nextDecl->acceptVisitor(this);
    return 0;
}

int VisitorASTprint::visit(node_NameDecl *nd) {
    if (nd->varType) nd->varType->acceptVisitor(this);
    if (nd->varName) {
        putTab();
        fprintf(stdout, "Variable Name: %s", nd->varName);
    }
    return 0;
}

int VisitorASTprint::visit(node_Type *t) {
    if (t->className) {
        putTab();
        fprintf(stdout, "Type: %s", t->className);
    } else {
        putTab();
        fprintf(stdout, "Type: ");
        printToken(t->primitiveType);
        if (t->isArray) fprintf(stdout, "[]");
    }
    return 0;
}

int VisitorASTprint::visit(node_StmtList *sl) {
    if (sl->stmt) sl->stmt->acceptVisitor(this);
    if (sl->nextStmt) sl->nextStmt->acceptVisitor(this);
    return 0;
}

int VisitorASTprint::visit(node_While *w) {
    putTab();
    fprintf(stdout, "node_While");
    tab++;
    if (w->condition) {
        putTab();
        fprintf(stdout, "Condition");
        tab++;
        w->condition->acceptVisitor(this);
        tab--;
    }
    if (w->stmtList) {
        putTab();
        fprintf(stdout, "Statement Block");
        tab++;
        w->stmtList->acceptVisitor(this);
        tab--;
    }
    tab--;
    return 0;
}

int VisitorASTprint::visit(node_If *i) {
    putTab();
    fprintf(stdout, "node_If");
    tab++;
    if (i->condition) {
        putTab();
        fprintf(stdout, "Condition");
        tab++;
        i->condition->acceptVisitor(this);
        tab--;
    }
    if (i->stmtList) {
        putTab();
        fprintf(stdout, "Statement Block");
        tab++;
        i->stmtList->acceptVisitor(this);
        tab--;
    }
    if (i->elseStmtList) {
        putTab();
        fprintf(stdout, "Else Statement Block");
        tab++;
        i->elseStmtList->acceptVisitor(this);
        tab--;
    }
    tab--;
    return 0;
}

int VisitorASTprint::visit(node_Return *r) {
    putTab();
    fprintf(stdout, "node_Return");
    tab++;
    if (r->exp) r->exp->acceptVisitor(this);
    tab--;
    return 0;
}

int VisitorASTprint::visit(node_Read *r) {
    putTab();
    fprintf(stdout, "node_Read");
    tab++;
    if (r->exp) r->exp->acceptVisitor(this);
    tab--;
    return 0;
}

int VisitorASTprint::visit(node_Println *p) {
    putTab();
    fprintf(stdout, "node_Println(");
    tab++;
    if (p->expList) {
        p->expList->acceptVisitor(this);
        putTab();
        fprintf(stdout, ")");
    }
    tab--;
    return 0;
}

int VisitorASTprint::visit(node_Assign *a) {
    putTab();
    fprintf(stdout, "node_Assign");
    tab++;
    if (a->Name) {
        putTab();
        fprintf(stdout, "Destination");
        tab++;
        a->Name->acceptVisitor(this);
        tab--;
    }
    if (a->assignmentExp) {
        putTab();
        fprintf(stdout, "Assignment Expression");
        tab++;
        a->assignmentExp->acceptVisitor(this);
        tab--;
    }
    tab--;
    return 0;
}

int VisitorASTprint::visit(node_Call *c) {
    putTab();
    fprintf(stdout, "node_Call");
    tab++;
    if (c->callMethod) c->callMethod->acceptVisitor(this);
    tab--;
    return 0;
}

int VisitorASTprint::visit(node_ExpList *el) {
    tab++;
    if (el->exp) el->exp->acceptVisitor(this);
    tab--;
    if (el->nextExp) el->nextExp->acceptVisitor(this);
    return 0;
}

int VisitorASTprint::visit(node_Name *n) {
    if (n->name) {
        putTab();
        fprintf(stdout, "Name: %s ", n->name);
    }
    if (n->sentParamsExpList) {
        fprintf(stdout, "(");
        putTab();
        fprintf(stdout, "Sent Parameters");
        n->sentParamsExpList->acceptVisitor(this);
        fprintf(stdout, ") ");
    }
    if (n->isThis) {
        putTab();
        printToken(THIS);
    }
    if (n->length) {
        fprintf(stdout, ".");
        printToken(LENGTH);
    }
    if (n->isArray) fprintf(stdout, "[]");

    if (n->index) n->index->acceptVisitor(this);

    if (n->nextName) {
        printToken(DOT);
        n->nextName->acceptVisitor(this);
    }

    return 0;
}

int VisitorASTprint::visit(node_New *n) {
    putTab();
    fprintf(stdout, "node_New");
    tab++;
    if (n->type) n->type->acceptVisitor(this);
    if (n->arrayLengthExp) {
        putTab();
        fprintf(stdout, "Array Limit Expression");
        tab++;
        n->arrayLengthExp->acceptVisitor(this);
        tab--;
    }
    tab--;

    return 0;
}

int VisitorASTprint::visit(node_Sign *s) {
    putTab();
    printToken(s->sign);
    tab++;
    if (s->exp) s->exp->acceptVisitor(this);
    tab--;

    return 0;
}

int VisitorASTprint::visit(node_Not *n) {
    putTab();
    printToken(NOT);
    tab++;
    if (n->exp) n->exp->acceptVisitor(this);
    tab--;

    return 0;
}

int VisitorASTprint::visit(node_BitwiseOP *b) {
    putTab();
    printToken(b->bitOp);
    tab++;
    if (b->leftExp) b->leftExp->acceptVisitor(this);
    if (b->rightExp) b->rightExp->acceptVisitor(this);
    tab--;

    return 0;
}

int VisitorASTprint::visit(node_BooleanOP *b) {
    putTab();
    printToken(b->boolOp);
    tab++;
    if (b->leftExp) b->leftExp->acceptVisitor(this);
    if (b->rightExp) b->rightExp->acceptVisitor(this);
    tab--;

    return 0;
}

int VisitorASTprint::visit(node_BinOp *b) {
    putTab();
    printToken(b->binOp);
    tab++;
    if (b->leftExp) b->leftExp->acceptVisitor(this);
    if (b->rightExp) b->rightExp->acceptVisitor(this);
    tab--;

    return 0;
}

int VisitorASTprint::visit(node_Bin2Op *b) {
    putTab();
    printToken(b->bin2Op);
    tab++;
    if (b->leftExp) b->leftExp->acceptVisitor(this);
    if (b->rightExp) b->rightExp->acceptVisitor(this);
    tab--;

    return 0;
}

int VisitorASTprint::visit(node_RelationalOP *r) {
    putTab();
    printToken(r->relOp);
    tab++;
    if (r->leftExp) r->leftExp->acceptVisitor(this);
    if (r->rightExp) r->rightExp->acceptVisitor(this);
    tab--;

    return 0;
}

int VisitorASTprint::visit(node_Number *n) {
    putTab();
    printToken(n->type);
    fprintf(stdout, " %s", n->number);

    return 0;
}

int VisitorASTprint::visit(node_Literal *l) {
    putTab();
    printToken(LITERAL);
    fprintf(stdout, ".%s", l->literal);

    return 0;
}

int VisitorASTprint::visit(node_Logic *l) {
    putTab();
    printToken(l->logic);

    return 0;
}

/*********************************************************************
 *              Implementação da Classe VisitorSemantic              *
 *********************************************************************/

VisitorSemantic::VisitorSemantic() {
    this->currentClass = NULL;
    this->declType = 0;
    this->currentParameter = NULL;
    this->currentAttribute = NULL;
    this->currentMethod = NULL;
    this->lastName = NULL;
    this->lastNameAssign = NULL;
    this->lastNameSymbolType = 0;
    this->arrayIdx = -1;
    this->hasReturn = false;
    for (int i = 0; i < VARIABLE_ARRAY_LEN; i++) this->variableArray[i] = NULL;
}

VisitorSemantic::~VisitorSemantic() {
    if (this->currentClass) this->currentClass = NULL;
    if (this->currentMethod) this->currentMethod = NULL;
    if (this->currentParameter) this->currentParameter = NULL;
    if (this->currentAttribute) this->currentAttribute = NULL;
    if (this->variableArray) delete this->variableArray;
}

int VisitorSemantic::visit(node_Program *p) {
    fprintf(stdout, "\n\n+==========================================================================+\n");
    fprintf(stdout, "|                           ANALISE SEMANTICA                              |\n");
    fprintf(stdout, "+==========================================================================+\n\n");

    if (p->classList) p->classList->acceptVisitor(this);

    MethodSymbol *main = (MethodSymbol*) IDTable.searchID((char*) "main", METHOD_SYMBOL);
    IDSymbol *aux = (IDSymbol*) main;
    int cont = 0;
    while (aux) {
        if (strcmp((char*) "main", IDTable.lexemes + main->getLexemeIdx()) == 0) {
            if (main->returnType->primitiveType == VOID && !main->firstParam) cont++;
        }
        aux = (IDSymbol*) main->next;
        while (aux) {
            if (aux->symbolType == METHOD_SYMBOL) {
                main = (MethodSymbol*) aux;
                break;
            } else aux = (IDSymbol*) aux->getNext();
        }
    }
    if (cont > 1) ErrorMessage(MAIN_EXISTS, 0);
    else if (cont == 0) ErrorMessage(MAIN_NOT_EXISTS, 0);
    IDTable.printTable();

    return 0;
}

int VisitorSemantic::visit(node_ClassList *cl) {

    ClassSymbol *aux = NULL, *auxExtends = NULL, *Class = NULL;
    aux = (ClassSymbol*) IDTable.searchID(cl->className, CLASS_SYMBOL);

    if (aux != NULL) ErrorMessage(CLASS_EXISTS, cl->line);
    else {
        if (cl->extendedClassName) {
            auxExtends = (ClassSymbol*) IDTable.searchID(cl->extendedClassName, CLASS_SYMBOL);
            if (!auxExtends) ErrorMessage(UNDECLARED_CLASS, cl->line);
        }
        Class = new ClassSymbol(0, NULL, CLASS_SYMBOL, auxExtends, NULL, NULL);
        IDTable.addSymbol((IDSymbol*) Class, cl->className);
        this->currentClass = Class;
    }
    this->declType = ATTRIBUTE_SYMBOL;
    if (cl->firstDecl) cl->firstDecl->acceptVisitor(this);
    if (cl->nextClass) cl->nextClass->acceptVisitor(this);

    return 0;
}

int VisitorSemantic::visit(node_VarList *vl) {
    /*
    lista de variáveis
    ...
     */
    if (vl->VarName) vl->VarName->acceptVisitor(this);
    if (vl->nextDecl) vl->nextDecl->acceptVisitor(this);

    return 0;
}

int VisitorSemantic::visit(node_MethodList *ml) {
    /*
    vai gravar o nome do método na tabela, junto com seus parametros e tipo de retorno
     */

    MethodSymbol *method = new MethodSymbol(0, NULL, METHOD_SYMBOL, NULL, NULL, NULL, NULL), *aux = NULL;
    Type *type = new Type(NULL, 0, 0);
    bool sameMethod = false;
    this->hasReturn = false;

    if (ml->returnType) {
        type->primitiveType = ml->returnType->primitiveType;
        if (ml->returnType->className) {
            type->Class = (char*) malloc((strlen(ml->returnType->className) + 1) * sizeof (char));
            strcpy(type->Class, ml->returnType->className);
        }
        if (ml->returnType->isArray) {
            type->arrayLimit = 0; //seta como 0, quer dizer que nao sabemos o tamanho do array ainda
        } else type->arrayLimit = -1; //seta como -1, quer dizer que nao e array
    }
    method->returnType = type;
    this->currentMethod = method;

    this->declType = PARAMETER_SYMBOL;
    if (ml->paramList) ml->paramList->acceptVisitor(this);
    this->declType = VARIABLE_SYMBOL;

    //Verifica se nao existe outro metodo na classe com a mesma assinatura que o metodo em construcao.
    aux = this->currentClass->firstMethod;
    while (aux != NULL) {
        bool sameReturnType;
        //se eles nao tiverem nomes iguais, nem testa nada
        if (strcmp(IDTable.lexemes + aux->getLexemeIdx(), ml->methodName) == 0) {
            if (aux->returnType->Class && method->returnType->Class) {
                if (strcmp(aux->returnType->Class, method->returnType->Class) == 0) sameReturnType = true;
                else sameReturnType = false;
            } else if (aux->returnType->Class || method->returnType->Class) sameReturnType = false;

            else {
                if (aux->returnType->primitiveType == method->returnType->primitiveType && aux->returnType->arrayLimit == method->returnType->arrayLimit) sameReturnType = true;
                else sameReturnType = false;
            }
            if (sameReturnType) {
                ParameterSymbol *param1 = (ParameterSymbol*) aux->firstParam;
                ParameterSymbol *param2 = method->firstParam;
                while (true) {
                    if (param1 == NULL && param2 == NULL) {
                        sameMethod = true;
                        break;
                    } else {
                        sameMethod = false;
                        break;
                    }
                }
            }
        }
        aux = aux->nextMethod;
    }
    if (sameMethod) ErrorMessage(METHOD_EXISTS, ml->line);
    aux = this->currentClass->firstMethod;
    if (aux == NULL) this->currentClass->firstMethod = method;
    else {
        while (true) {
            if (aux->nextMethod) aux = aux->nextMethod;
            else break;
        }
        aux->nextMethod = method;
    }
    //visita o stms do metodo
    if (ml->stmtList) ml->stmtList->acceptVisitor(this);

    method->owner = this->currentClass;
    //salva na tabela o metodo
    IDTable.addSymbol((IDSymbol*) method, ml->methodName);
    //variavel utilizada na gravacao dos atributos de classe
    this->declType = ATTRIBUTE_SYMBOL;

    //metodo static void nao precisa ter return, o resto sim
    if (ml->returnType->primitiveType != VOID && !this->hasReturn) ErrorMessage(AUSENT_RETURN, ml->line);

    //visita a proxima declaracao, metodo ou novo atributo
    if (ml->nextDecl) ml->nextDecl->acceptVisitor(this);

    return 0;
}

int VisitorSemantic::visit(node_NameDecl *nd) {
    Type *type = new Type(NULL, 0, 0);
    ClassSymbol *classaux = NULL;
    VariableSymbol *var = new VariableSymbol(0, NULL, type, VARIABLE_SYMBOL, 0, false, NULL);
    ParameterSymbol *param = NULL, *aux = NULL;
    AttributeSymbol *att = NULL, *aux2 = NULL;
    VariableSymbol *varaux = NULL;
    VariableSymbolList *listaux = NULL;

    if (nd->varType) {
        var->type->primitiveType = nd->varType->primitiveType;
        if (nd->varType->isArray) var->type->arrayLimit = 0;
        else var->type->arrayLimit = -1;
        if (nd->varType->className) {
            classaux = (ClassSymbol*) IDTable.searchID(nd->varType->className, CLASS_SYMBOL);
            if (!classaux) ErrorMessage(UNDECLARED_CLASS, nd->line);
            var->type->Class = (char*) malloc((strlen(nd->varType->className) + 1) * sizeof (char));
            strcpy(var->type->Class, nd->varType->className);
        }
    }

    switch (this->declType) {
        case VARIABLE_SYMBOL:
            var->scope = 1;
            var->active = 1;
            var->symbolType = VARIABLE_SYMBOL;
            if (this->arrayIdx >= 0) {
                if (this->variableArray[this->arrayIdx]) {
                    listaux = this->variableArray[this->arrayIdx];
                    while (listaux) {
                        if (strcmp(IDTable.lexemes + listaux->thisVar->getLexemeIdx(), nd->varName) == 0) {
                            ErrorMessage(VAR_EXISTS, nd->line);
                            break;
                        }
                        listaux = listaux->nextVar;
                    }
                }
            } else {
                aux = this->currentMethod->firstParam;
                while (aux) {
                    if (strcmp(IDTable.lexemes + aux->getLexemeIdx(), nd->varName) == 0) {
                        ErrorMessage(PARAMETER_EXISTS, nd->line);
                        break;
                    }
                    aux = aux->nextParam;
                }
                varaux = this->currentMethod->firstVariable;
                while (varaux) {
                    if (strcmp(IDTable.lexemes + varaux->getLexemeIdx(), nd->varName) == 0 && varaux->active) {
                        ErrorMessage(VAR_EXISTS, nd->line);
                        break;
                    }
                    varaux = varaux->nextVariable;
                }
            }
            IDTable.addSymbol((IDSymbol*) var, nd->varName);
            if (!this->currentMethod->firstVariable) this->currentMethod->firstVariable = var;
            else {
                varaux = this->currentMethod->firstVariable;
                while (varaux->nextVariable) varaux = varaux->nextVariable;
                varaux->nextVariable = var;
            }
            break;

        case PARAMETER_SYMBOL:
            var->scope = 1;
            var->active = 1;
            var->symbolType = PARAMETER_SYMBOL;
            param = new ParameterSymbol(0, NULL, PARAMETER_SYMBOL, var, NULL);
            aux = this->currentMethod->firstParam;
            while (aux) {
                if (strcmp(IDTable.lexemes + aux->getLexemeIdx(), nd->varName) == 0) {
                    ErrorMessage(PARAMETER_EXISTS, nd->line);
                    break;
                }
                aux = aux->nextParam;
            }
            param->owner = this->currentMethod;
            IDTable.addSymbol((IDSymbol*) param, nd->varName);
            if (!this->currentMethod->firstParam) this->currentMethod->firstParam = param;
            else {
                ParameterSymbol *aux = this->currentMethod->firstParam;
                while (aux->nextParam) aux = aux->nextParam;
                aux->nextParam = param;
            }
            break;

        case ATTRIBUTE_SYMBOL:
            var->scope = 0;
            var->active = 1;
            var->symbolType = ATTRIBUTE_SYMBOL;
            att = new AttributeSymbol(0, NULL, ATTRIBUTE_SYMBOL, var, NULL);
            aux2 = this->currentClass->firstAttribute;
            while (aux2) {
                if (strcmp(IDTable.lexemes + aux2->getLexemeIdx(), nd->varName) == 0) {
                    ErrorMessage(ATTRIBUTE_EXISTS, nd->line);
                    break;
                }
                aux2 = aux2->nextAttribute;
            }
            att->owner = this->currentClass;
            IDTable.addSymbol((IDSymbol*) att, nd->varName);
            if (!this->currentClass->firstAttribute) this->currentClass->firstAttribute = att;
            else {
                AttributeSymbol *aux = this->currentClass->firstAttribute;
                while (aux->nextAttribute) aux = aux->nextAttribute;
                aux->nextAttribute = att;
            }
            break;
    }

    if (this->arrayIdx >= 0) {
        if (this->variableArray[this->arrayIdx]) {
            listaux = this->variableArray[this->arrayIdx];
            while (listaux->nextVar) listaux = listaux->nextVar;
            VariableSymbolList *addListAux = new VariableSymbolList(var, NULL);
            listaux->nextVar = addListAux;
        } else {
            VariableSymbolList *addListAux = new VariableSymbolList(var, NULL);
            this->variableArray[this->arrayIdx] = addListAux;
        }
    }

    this->lastName = NULL;
    return 0;
}

int VisitorSemantic::visit(node_Type *t) {
    return 0;
}

int VisitorSemantic::visit(node_StmtList *sl) {
    if (sl->stmt) sl->stmt->acceptVisitor(this);
    if (sl->nextStmt) sl->nextStmt->acceptVisitor(this);
    return 0;
}

int VisitorSemantic::visit(node_While *w) {
    /*
    verifica se a expressão é do tipo boolean
     */
    int t;
    if (w->condition) t = w->condition->acceptVisitor(this);
    if (t != LOGICAL) ErrorMessage(INCOMPATIBLE_TYPES_LOOP, w->line);
    if (w->stmtList) {
        this->arrayIdx++;
        this->variableArray[this->arrayIdx] = NULL;

        w->stmtList->acceptVisitor(this);

        VariableSymbolList *auxList;
        auxList = this->variableArray[this->arrayIdx];

        while (auxList) {
            auxList->thisVar->active = false;
            auxList->thisVar = NULL;
            auxList = auxList->nextVar;
        }
        this->arrayIdx--;
    }

    return 0;
}

int VisitorSemantic::visit(node_If *i) {
    /*
    verifica se a expressão é do tipo boolean
     */
    int t;
    if (i->condition) t = i->condition->acceptVisitor(this);
    if (t != LOGICAL) ErrorMessage(INCOMPATIBLE_TYPES_LOOP, i->line);
    if (i->stmtList) {
        this->arrayIdx++;
        this->variableArray[this->arrayIdx] = NULL;

        i->stmtList->acceptVisitor(this);

        VariableSymbolList *auxList;
        auxList = this->variableArray[this->arrayIdx];

        while (auxList) {
            auxList->thisVar->active = false;
            auxList->thisVar = NULL;
            auxList = auxList->nextVar;
        }
        this->arrayIdx--;
    }

    if (i->elseStmtList) {
        this->arrayIdx++;
        this->variableArray[this->arrayIdx] = NULL;

        i->elseStmtList->acceptVisitor(this);

        VariableSymbolList *auxList;
        auxList = this->variableArray[this->arrayIdx];

        while (auxList) {
            auxList->thisVar->active = false;
            auxList->thisVar = NULL;
            auxList = auxList->nextVar;
        }
        this->arrayIdx--;
    }

    return 0;
}

int VisitorSemantic::visit(node_Return *r) {
    /*
    verifica se o tipo do return, é o tipo especificado no cabeçalho
     */
    int type;
    this->hasReturn = true;
    if (r->exp) type = r->exp->acceptVisitor(this);
    if (type == LOGICAL || type == REALNUM || type == INTNUM || type == REALVECTOR || type == INTVECTOR) {
        if (type != this->currentMethod->returnType->primitiveType)
            ErrorMessage(INCOMPATIBLE_TYPES_RETURN, r->line);
    } else {
        if (type == LITERAL) ErrorMessage(INCOMPATIBLE_TYPES_RETURN, r->line);
        else {
            char* Class = (char*) type;
            if (this->currentMethod->returnType->Class)
                if (strcmp(Class, this->currentMethod->returnType->Class) != 0) ErrorMessage(INCOMPATIBLE_TYPES_RETURN, r->line);
        }
    }

    return 0;
}

int VisitorSemantic::visit(node_Read *r) {
    int t;
    if (r->exp) t = r->exp->acceptVisitor(this);
    if (t != INTNUM && t != REALNUM && t != LOGICAL) ErrorMessage(INVALID_READ_EXPRESSION, r->line);
    return 0;
}

int VisitorSemantic::visit(node_Println *p) {
    node_ExpList *aux = p->expList;
    while (aux) {
        int expType;
        expType = aux->exp->acceptVisitor(this);

        if (!(expType == LOGICAL || expType == LITERAL || expType == REALNUM || expType == INTNUM))
            ErrorMessage(INVALID_PRINT_EXPRESSION, p->line);
        aux = aux->nextExp;
    }
    if (p->expList) p->expList->acceptVisitor(this);

    return 0;
}

int VisitorSemantic::visit(node_Assign *a) {
    /*
    faz a verificação de tipos
    se o tipo da exp de origem é compatível com o tipo da variável destino
     */
    int typeDST, typeSRC;
    char *ClassNameDST = NULL, *ClassNameSRC = NULL;
    //if (typeid (*a->assignmentExp) == typeid (node_New)) this->inNew = true;
    this->inNew = true;
    if (a->Name) typeDST = a->Name->acceptVisitor(this);
    this->inNew = false;
    if (a->assignmentExp) typeSRC = a->assignmentExp->acceptVisitor(this);


    //se a expressao for um new, setar o tamanho do arranjo como 0, ou seja, foi inicializado
    if (typeid (*a->assignmentExp) == typeid (node_New) && (typeDST == INTVECTOR || typeDST == REALVECTOR)) {
        AttributeSymbol* aux = (AttributeSymbol*) IDTable.searchID(this->lastNameAssign->name, ATTRIBUTE_SYMBOL);
        if (aux) aux->attribute->type->arrayLimit = 0;
    }

    if (typeid (*a->assignmentExp) == typeid (node_New) && (typeDST != 0)) {
        VariableSymbol* auxVar;
        auxVar = (VariableSymbol*) IDTable.searchID(a->Name->name, VARIABLE_SYMBOL);
        auxVar->defined = true;
    }

    switch (typeSRC) {
        case LITERAL:
            ErrorMessage(INCOMPATIBLE_TYPES, a->line);
            break;

        case INTNUM:
            if (typeDST != INTNUM && typeDST != REALNUM) ErrorMessage(INCOMPATIBLE_TYPES, a->line);
            break;

        case REALNUM:
            if (typeDST != REALNUM) ErrorMessage(INCOMPATIBLE_TYPES, a->line);
            break;

        case LOGICAL:
            if (typeDST != LOGICAL) ErrorMessage(INCOMPATIBLE_TYPES, a->line);
            break;

        case REALVECTOR:
            if (typeDST != REALVECTOR) ErrorMessage(INCOMPATIBLE_TYPES, a->line);
            break;

        case INTVECTOR:
            if (typeDST != INTVECTOR) ErrorMessage(INCOMPATIBLE_TYPES, a->line);
            break;

            //verifica se as classes sao iguais
        default:
            if (typeSRC != 0) {
                ClassNameSRC = (char*) typeSRC;
                ClassNameDST = (char*) typeDST;
                if (strcmp(ClassNameDST, ClassNameSRC) != 0) {
                    ErrorMessage(INCOMPATIBLE_TYPES, a->line);
                    break;
                }
            }
    }

    return 0;
}

int VisitorSemantic::visit(node_Call *c) {
    if (c->callMethod) c->callMethod->acceptVisitor(this);

    return 0;
}

int VisitorSemantic::visit(node_ExpList *el) {
    if (el->exp) el->exp->acceptVisitor(this);
    if (el->nextExp) el->nextExp->acceptVisitor(this);

    return 0;
}

int VisitorSemantic::visit(node_Name *n) {
    /*
    verifica se já foi declarado, se é arranjo...

     */
    VariableSymbol *variable = NULL;
    MethodSymbol *method = NULL;
    AttributeSymbol *attribute = NULL;
    ClassSymbol *Class = NULL;
    switch (this->lastNameSymbolType) {
        case METHOD_SYMBOL:
            method = (MethodSymbol*)this->lastName;
            break;
        case VARIABLE_SYMBOL:
            variable = (VariableSymbol*)this->lastName;
            break;
        case ATTRIBUTE_SYMBOL:
            attribute = (AttributeSymbol*)this->lastName;
            break;
    }

    if (n->isThis) {
        this->setLastNameValues(n, (IDSymbol*) new VariableSymbol(0, NULL, (Type*)new Type(IDTable.lexemes + this->currentClass->getLexemeIdx(), 0, 0), VARIABLE_SYMBOL, 1, true, NULL), VARIABLE_SYMBOL);
        return n->nextName->acceptVisitor(this);
    } else if (n->length) {
        if (variable) {
            if (variable->type->arrayLimit == -1) ErrorMessage(BAD_USE_LENGTH, n->line);
        } else {
            if (method) {
                if (method->returnType->arrayLimit == -1) ErrorMessage(BAD_USE_LENGTH, n->line);
            } else {
                if (attribute) {
                    if (attribute->attribute->type->arrayLimit == -1) ErrorMessage(BAD_USE_LENGTH, n->line);
                }
            }
        }
        this->setLastNameValues(n, NULL, 0);
        return INTNUM;
    } else if (n->isArray) {
        //isArray quando existem [], ou seja, indice acessado
        if (method) {
            ClassSymbol* classAux;
            AttributeSymbol* attAux;
            if (method->returnType->Class) classAux = (ClassSymbol*) IDTable.searchID(method->returnType->Class, CLASS_SYMBOL);
            while (classAux) {
                attAux = classAux->firstAttribute;
                while (attAux) {
                    if (strcmp(IDTable.lexemes + attAux->getLexemeIdx(), n->name) == 0) {
                        if (REALVECTOR == attAux->attribute->type->primitiveType || INTVECTOR == attAux->attribute->type->primitiveType) {
                            if (n->nextName) {
                                this->setLastNameValues(n, (IDSymbol*) attAux, ATTRIBUTE_SYMBOL);
                                n->tabLink = (IDSymbol*) attAux;
                                n->tabLinkType = ATTRIBUTE_SYMBOL;
                                return n->nextName->acceptVisitor(this);
                            } else {
                                this->setLastNameValues(n, NULL, 0);
                                switch (attAux->attribute->type->primitiveType) {
                                    case INTVECTOR:
                                        n->tabLink = (IDSymbol*) attAux;
                                        n->tabLinkType = ATTRIBUTE_SYMBOL;
                                        return INTNUM;
                                        break;

                                    case REALVECTOR:
                                        n->tabLink = (IDSymbol*) attAux;
                                        n->tabLinkType = ATTRIBUTE_SYMBOL;
                                        return REALNUM;
                                        break;
                                }
                            }
                        } else {
                            ErrorMessage(INCOMPATIBLE_TYPES, n->line);
                            this->setLastNameValues(n, NULL, 0);
                            return 0;
                        }
                    }
                    attAux = attAux->nextAttribute;
                }
                classAux = classAux->extendedClass;
            }
            //nao achou nenhum atributo na classe resposta do método, compativel com o name atual
            ErrorMessage(INVALID_NAME, n->line);
            return 0;
        } else if (variable) {
            ClassSymbol *aux;
            aux = (ClassSymbol*) IDTable.searchID(variable->type->Class, CLASS_SYMBOL);
            //se a classe existe, procuro nos atributos dela por um arranjo com o mesmo nome
            while (aux) {
                AttributeSymbol *attAux;
                attAux = aux->firstAttribute;
                bool seemsEQ = false;
                while (attAux) {
                    //se o name atual tem o mesmo nome que o atributo atual do laço...
                    if (strcmp(IDTable.lexemes + attAux->getLexemeIdx(), n->name) == 0) {
                        seemsEQ = true;
                        //se existe um atributo com o mesmo nome, mas não é arranjo, ERRO
                        if (attAux->attribute->type->arrayLimit == -1) ErrorMessage(INCOMPATIBLE_TYPES_ASSIGN, n->line);
                        else {
                            int idx_type;
                            node_Name *aux = this->lastNameAssign;
                            IDSymbol *aux2 = this->lastName;
                            int aux3 = this->lastNameSymbolType;
                            this->setLastNameValues(n, NULL, 0);
                            idx_type = n->index->acceptVisitor(this);
                            this->setLastNameValues(aux, aux2, aux3);
                            //verifica o tipo da expressao do indice acessado, se nao for INT, da erro
                            if (idx_type != INTNUM) ErrorMessage(INVALID_EXPRESSION_IDX, n->line);
                            if (n->nextName) {
                                this->setLastNameValues(n, (IDSymbol*) attAux, ATTRIBUTE_SYMBOL);
                                n->tabLink = (IDSymbol*) attAux;
                                n->tabLinkType = ATTRIBUTE_SYMBOL;
                                return n->nextName->acceptVisitor(this);
                            } else {
                                this->setLastNameValues(n, NULL, 0);
                                switch (attAux->attribute->type->primitiveType) {
                                    case INTVECTOR:
                                        n->tabLink = (IDSymbol*) attAux;
                                        n->tabLinkType = ATTRIBUTE_SYMBOL;
                                        return INTNUM;
                                        break;

                                    case REALVECTOR:
                                        n->tabLink = (IDSymbol*) attAux;
                                        n->tabLinkType = ATTRIBUTE_SYMBOL;
                                        return REALNUM;
                                        break;
                                }
                            }
                        }
                    }
                    attAux = attAux->nextAttribute;
                }
                //se ele é metodo, quer dizer que o arranjo é retorno de um método que pertece a uma variavel (name anterior).
                if (n->isMethod) {
                    //verificar se o método pertence ao variable anterior
                    MethodSymbol* methodAux;
                    methodAux = aux->firstMethod;
                    while (methodAux) {
                        //acha metodo com mesmo nome
                        if (strcmp(IDTable.lexemes + methodAux->getLexemeIdx(), n->name) == 0) {
                            ParameterSymbol *param = methodAux->firstParam;
                            node_ExpList *expList = n->sentParamsExpList;
                            //se o metodo encontrado e o atual nao precisam de parametros:
                            if (param == NULL && expList == NULL) {
                                if (n->nextName) {
                                    //se existe outra declaracao depois da atual
                                    this->setLastNameValues(n, (IDSymbol*) methodAux, METHOD_SYMBOL);
                                    n->tabLink = (IDSymbol*) methodAux;
                                    n->tabLinkType = METHOD_SYMBOL;
                                    return n->nextName->acceptVisitor(this);
                                } else {
                                    //se nao existe, retorna um objeto primitivo ou classe especifica
                                    if (methodAux->returnType->Class) {
                                        this->setLastNameValues(n, NULL, 0);
                                        n->tabLink = (IDSymbol*) methodAux;
                                        n->tabLinkType = METHOD_SYMBOL;
                                        return (int) methodAux->returnType->Class;
                                    } else {
                                        this->setLastNameValues(n, NULL, 0);
                                        if (n->index) {
                                            switch (methodAux->returnType->primitiveType) {
                                                case INTVECTOR:
                                                    n->tabLink = (IDSymbol*) methodAux;
                                                    n->tabLinkType = METHOD_SYMBOL;
                                                    return INTNUM;
                                                    break;

                                                case REALVECTOR:
                                                    n->tabLink = (IDSymbol*) methodAux;
                                                    n->tabLinkType = METHOD_SYMBOL;
                                                    return REALNUM;
                                                    break;
                                            }
                                        }
                                        n->tabLink = (IDSymbol*) methodAux;
                                        n->tabLinkType = METHOD_SYMBOL;
                                        return methodAux->returnType->primitiveType;
                                    }
                                }
                            }
                            //verifica parametro por parametro para confirmar assinatura do metodo
                            while (expList) {
                                int t;
                                //armazena atributos locais antes de chamar o visitor dos parametros, e os recupera depois
                                node_Name* auxNameAssign = this->lastNameAssign;
                                IDSymbol* auxLastName = this->lastName;
                                int auxLastNameSType = this->lastNameSymbolType;
                                this->setLastNameValues(NULL, NULL, 0);

                                t = expList->exp->acceptVisitor(this);

                                this->setLastNameValues(auxNameAssign, auxLastName, auxLastNameSType);

                                if (param->param->type->Class) {
                                    if (strcmp((char*) t, param->param->type->Class) == 0) seemsEQ = true;
                                    else seemsEQ = false;
                                } else {
                                    if (param->param->type->primitiveType == t) seemsEQ = true;
                                    else seemsEQ = false;
                                }
                                //se os parametros sao iguais, continua a verificacao, se nao, ja pode parar com break
                                if (seemsEQ) {
                                    if (expList->nextExp && param->nextParam) {
                                        expList = expList->nextExp;
                                        param = param->nextParam;
                                    } else {
                                        if (expList->nextExp || param->nextParam) {
                                            seemsEQ = false;
                                            break;
                                        } else {
                                            //se terminaram os parametros atuais e do metodo buscado, entao eles sao iguais, pode parar o while
                                            seemsEQ = true;
                                            break;
                                        }
                                    }
                                } else break;
                            }
                            //se terminou o while de explist e seemsEQ é verdadeiro, entao encontramos um metodo com a assinatura atual
                            if (seemsEQ) {
                                if (n->nextName) {
                                    this->setLastNameValues(n, (IDSymbol*) methodAux, METHOD_SYMBOL);
                                    n->tabLink = (IDSymbol*) methodAux;
                                    n->tabLinkType = METHOD_SYMBOL;
                                    return n->nextName->acceptVisitor(this);
                                } else {
                                    if (methodAux->returnType->Class) {
                                        this->setLastNameValues(n, NULL, 0);
                                        n->tabLink = (IDSymbol*) methodAux;
                                        n->tabLinkType = METHOD_SYMBOL;
                                        return (int) methodAux->returnType->Class;
                                    } else {
                                        this->setLastNameValues(n, NULL, 0);

                                        if (n->index) {
                                            switch (methodAux->returnType->primitiveType) {
                                                case INTVECTOR:
                                                    n->tabLink = (IDSymbol*) methodAux;
                                                    n->tabLinkType = METHOD_SYMBOL;
                                                    return INTNUM;
                                                    break;

                                                case REALVECTOR:
                                                    n->tabLink = (IDSymbol*) methodAux;
                                                    n->tabLinkType = METHOD_SYMBOL;
                                                    return REALNUM;
                                                    break;
                                            }
                                        }
                                        n->tabLink = (IDSymbol*) methodAux;
                                        n->tabLinkType = METHOD_SYMBOL;
                                        return methodAux->returnType->primitiveType;
                                    }
                                }
                            }
                        }
                        methodAux = methodAux->nextMethod;
                    }
                }
                aux = aux->extendedClass;
            }
            ErrorMessage(UNDECLARED_VAR, n->line);
            this->setLastNameValues(n, NULL, 0);
            return 0;
        } else if (attribute) {
            //se o antecessor é um atributo, deve ser uma classe, entao procuro nos atributos desta classe um arranjo como o atual
            ClassSymbol *aux;
            aux = (ClassSymbol*) IDTable.searchID(attribute->attribute->type->Class, CLASS_SYMBOL);
            while (aux) {
                AttributeSymbol *attAux;
                attAux = aux->firstAttribute;
                bool varFound = false;
                while (attAux) {
                    if (strcmp(IDTable.lexemes + attAux->getLexemeIdx(), n->name) == 0) {
                        varFound = true;
                        if (attAux->attribute->type->arrayLimit != -1)
                            ErrorMessage(INCOMPATIBLE_TYPES_ASSIGN, n->line);
                        else {
                            int t;
                            t = n->index->acceptVisitor(this);
                            if (t != INTNUM) ErrorMessage(INVALID_EXPRESSION_IDX, n->line);
                            if (n->nextName) {
                                this->setLastNameValues(n, (IDSymbol*) attAux, ATTRIBUTE_SYMBOL);
                                n->tabLink = (IDSymbol*) attAux;
                                n->tabLinkType = ATTRIBUTE_SYMBOL;
                                return n->nextName->acceptVisitor(this);
                            } else {
                                this->setLastNameValues(n, NULL, 0);
                                switch (attAux->attribute->type->primitiveType) {
                                    case INTVECTOR:
                                        n->tabLink = (IDSymbol*) attAux;
                                        n->tabLinkType = ATTRIBUTE_SYMBOL;
                                        return INTNUM;
                                        break;

                                    case REALVECTOR:
                                        n->tabLink = (IDSymbol*) attAux;
                                        n->tabLinkType = ATTRIBUTE_SYMBOL;
                                        return REALNUM;
                                        break;
                                }
                            }
                        }
                    }
                    attAux = attAux->nextAttribute;
                }
                aux = aux->extendedClass;
            }
            ErrorMessage(UNDECLARED_VAR, n->line);
            this->setLastNameValues(n, NULL, 0);
            return 0;

        } else {
            //testar names aqui SEM um name antecessor, ou seja, parametros e variaveis locais
            IDSymbol *symbolAux;
            //variaveis locais
            symbolAux = IDTable.searchID(n->name, VARIABLE_SYMBOL);
            VariableSymbol* auxAttArray;
            while (symbolAux) {
                if (symbolAux->symbolType == VARIABLE_SYMBOL) {
                    auxAttArray = (VariableSymbol*) symbolAux;
                    if (auxAttArray->active && auxAttArray->type->arrayLimit != -1) {
                        if (!this->inNew && !auxAttArray->defined) ErrorMessage(UNDEFINED_VARIABLE, n->line);
                        if (n->nextName) {
                            this->setLastNameValues(n, (IDSymbol*) auxAttArray, VARIABLE_SYMBOL);
                            n->tabLink = (IDSymbol*) auxAttArray;
                            n->tabLinkType = VARIABLE_SYMBOL;
                            return n->nextName->acceptVisitor(this);
                        } else {
                            this->setLastNameValues(n, NULL, 0);
                            switch (auxAttArray->type->primitiveType) {
                                case INTVECTOR:
                                    n->tabLink = (IDSymbol*) auxAttArray;
                                    n->tabLinkType = VARIABLE_SYMBOL;
                                    return INTNUM;
                                    break;

                                case REALVECTOR:
                                    n->tabLink = (IDSymbol*) auxAttArray;
                                    n->tabLinkType = VARIABLE_SYMBOL;
                                    return REALNUM;
                                    break;
                            }
                        }
                    }
                }
                symbolAux = (IDSymbol*) symbolAux->getNext();
            }
            //parametros
            symbolAux = IDTable.searchID(n->name, PARAMETER_SYMBOL);
            ParameterSymbol *paramaux = this->currentMethod->firstParam;
            while (paramaux) {
                if (n->nextName) {
                    this->setLastNameValues(n, (VariableSymbol*) paramaux->param, VARIABLE_SYMBOL);
                    n->tabLink = (IDSymbol*) paramaux;
                    n->tabLinkType = PARAMETER_SYMBOL;
                    return n->nextName->acceptVisitor(this);
                } else {
                    if (paramaux->param->type->Class) {
                        this->setLastNameValues(n, NULL, 0);
                        n->tabLink = (IDSymbol*) paramaux;
                        n->tabLinkType = PARAMETER_SYMBOL;
                        return (int) paramaux->param->type->Class;
                    } else {
                        this->setLastNameValues(n, NULL, 0);
                        n->tabLink = (IDSymbol*) paramaux;
                        n->tabLinkType = PARAMETER_SYMBOL;
                        if (n->index) {
                            switch (paramaux->param->type->primitiveType) {
                                case INTVECTOR:
                                    return INTNUM;

                                case REALVECTOR:
                                    return REALNUM;
                            }
                        } else return paramaux->param->type->primitiveType;
                    }
                }
                paramaux = paramaux->nextParam;
            }
            ErrorMessage(UNDECLARED_VAR, n->line);
            this->setLastNameValues(n, NULL, 0);
            return 0;
        }
    } else if (n->isMethod) {
        //se o name atual é um método, é preciso verificar se o antecessor possui um método como ele
        if (variable) Class = (ClassSymbol*) IDTable.searchID(variable->type->Class, CLASS_SYMBOL);
        else if (method) Class = (ClassSymbol*) IDTable.searchID(method->returnType->Class, CLASS_SYMBOL);
        else if (attribute) Class = (ClassSymbol*) IDTable.searchID(attribute->attribute->type->Class, CLASS_SYMBOL);
            //se nao tiver antecessor, finge que é this
        else Class = (ClassSymbol*) IDTable.searchID(IDTable.lexemes + this->currentClass->getLexemeIdx(), CLASS_SYMBOL);
        while (Class) {
            bool seemsEQ = false;
            MethodSymbol *methodAux = Class->firstMethod;
            //procura por um metodo como o atual na classe encontrada atraves do antecessor
            while (methodAux) {
                if (strcmp(IDTable.lexemes + methodAux->getLexemeIdx(), n->name) == 0) {
                    ParameterSymbol *param = methodAux->firstParam;
                    node_ExpList *expList = n->sentParamsExpList;
                    //se o metodo encontrado e o atual nao precisam de parametros:
                    if (param == NULL && expList == NULL) {
                        if (n->nextName) {
                            //se existe outra declaracao depois da atual
                            this->setLastNameValues(n, (IDSymbol*) methodAux, METHOD_SYMBOL);
                            n->tabLink = (IDSymbol*) methodAux;
                            n->tabLinkType = METHOD_SYMBOL;
                            return n->nextName->acceptVisitor(this);
                        } else {
                            //se nao existe, retorna um objeto primitivo ou classe especifica
                            if (methodAux->returnType->Class) {
                                this->setLastNameValues(n, NULL, 0);
                                n->tabLink = (IDSymbol*) methodAux;
                                n->tabLinkType = METHOD_SYMBOL;
                                return (int) methodAux->returnType->Class;
                            } else {
                                this->setLastNameValues(n, NULL, 0);
                                n->tabLink = (IDSymbol*) methodAux;
                                n->tabLinkType = METHOD_SYMBOL;
                                return methodAux->returnType->primitiveType;
                            }
                        }
                    }
                    if (param == NULL || expList == NULL) seemsEQ = false;
                    else {
                        //verifica parametro por parametro para confirmar assinatura do metodo
                        while (expList) {
                            int t;

                            //armazena atributos locais antes de chamar o visitor dos parametros, e os recupera depois
                            node_Name* auxNameAssign = this->lastNameAssign;
                            IDSymbol* auxLastName = this->lastName;
                            int auxLastNameSType = this->lastNameSymbolType;
                            this->setLastNameValues(NULL, NULL, 0);

                            t = expList->exp->acceptVisitor(this);

                            this->lastNameAssign = auxNameAssign;
                            this->lastName = auxLastName;
                            this->lastNameSymbolType = auxLastNameSType;
                            if (param->param->type->Class) {
                                if (strcmp((char*) t, param->param->type->Class) == 0) seemsEQ = true;
                                else seemsEQ = false;
                            } else {
                                if (param->param->type->primitiveType == t) seemsEQ = true;
                                else seemsEQ = false;
                            }
                            //se os parametros sao iguais, continua a verificacao, se nao, ja pode parar com break
                            if (seemsEQ) {
                                if (expList->nextExp && param->nextParam) {
                                    expList = expList->nextExp;
                                    param = param->nextParam;
                                } else {
                                    if (expList->nextExp || param->nextParam) {
                                        seemsEQ = false;
                                        break;
                                    } else {
                                        //se terminaram os parametros atuais e do metodo buscado, entao eles sao iguais, pode parar o while
                                        seemsEQ = true;
                                        break;
                                    }
                                }
                            } else break;
                        }
                        //se terminou o while de explist e seemsEQ é verdadeiro, entao encontramos um metodo com a assinatura atual
                        if (seemsEQ) {
                            if (n->nextName) {
                                this->setLastNameValues(n, (IDSymbol*) methodAux, METHOD_SYMBOL);
                                n->tabLink = (IDSymbol*) methodAux;
                                n->tabLinkType = METHOD_SYMBOL;
                                return n->nextName->acceptVisitor(this);
                            } else {
                                if (methodAux->returnType->Class) {
                                    this->setLastNameValues(n, NULL, 0);
                                    n->tabLink = (IDSymbol*) methodAux;
                                    n->tabLinkType = METHOD_SYMBOL;
                                    return (int) methodAux->returnType->Class;
                                } else {
                                    this->setLastNameValues(n, NULL, 0);
                                    n->tabLink = (IDSymbol*) methodAux;
                                    n->tabLinkType = METHOD_SYMBOL;
                                    return methodAux->returnType->primitiveType;
                                }
                            }
                        }
                    }
                }
                methodAux = methodAux->nextMethod;
            }
            Class = Class->extendedClass;
        }
        ErrorMessage(UNDECLARED_METHOD, n->line);
        this->setLastNameValues(n, NULL, 0);
        return 0;
    } else { //Name atual nao eh metodo, nem array, nem length, nem this
        if (this->lastName) {
            if (variable) Class = (ClassSymbol*) IDTable.searchID(variable->type->Class, CLASS_SYMBOL);
            else if (method) Class = (ClassSymbol*) IDTable.searchID(method->returnType->Class, CLASS_SYMBOL);
            else if (attribute) Class = (ClassSymbol*) IDTable.searchID(attribute->attribute->type->Class, CLASS_SYMBOL);
                //se nao tem antecessor, finge que é this
            else Class = (ClassSymbol*) IDTable.searchID(IDTable.lexemes + this->currentClass->getLexemeIdx(), CLASS_SYMBOL);
            while (Class) {
                AttributeSymbol *attributeaux = Class->firstAttribute;
                while (attributeaux) {
                    if (strcmp(n->name, IDTable.lexemes + attributeaux->getLexemeIdx()) == 0) {
                        if (n->nextName) {
                            this->setLastNameValues(n, (IDSymbol*) attributeaux, ATTRIBUTE_SYMBOL);
                            n->tabLink = (IDSymbol*) attributeaux;
                            n->tabLinkType = ATTRIBUTE_SYMBOL;
                            return n->nextName->acceptVisitor(this);
                        } else {
                            if (attributeaux->attribute->type->Class) {
                                this->setLastNameValues(n, NULL, 0);
                                n->tabLink = (IDSymbol*) attributeaux;
                                n->tabLinkType = ATTRIBUTE_SYMBOL;
                                return (int) attributeaux->attribute->type->Class;
                            } else {
                                this->setLastNameValues(n, NULL, 0);
                                n->tabLink = (IDSymbol*) attributeaux;
                                n->tabLinkType = ATTRIBUTE_SYMBOL;
                                return attributeaux->attribute->type->primitiveType;
                            }
                        }
                    }
                    attributeaux = attributeaux->nextAttribute;
                }
                Class = Class->extendedClass;
            }
            ErrorMessage(UNDECLARED_VAR, n->line);
            this->setLastNameValues(n, NULL, 0);
            return 0;
        } else {
            IDSymbol *symbolAux;
            symbolAux = IDTable.searchID(n->name, VARIABLE_SYMBOL);
            VariableSymbol *varaux;
            while (symbolAux) {
                if (symbolAux->symbolType == VARIABLE_SYMBOL) {
                    varaux = (VariableSymbol*) symbolAux;
                    if (varaux->active) {
                        if (n->nextName) {
                            this->setLastNameValues(n, (IDSymbol*) varaux, VARIABLE_SYMBOL);
                            n->tabLink = (IDSymbol*) varaux;
                            n->tabLinkType = VARIABLE_SYMBOL;
                            if (!this->inNew && !varaux->defined) ErrorMessage(UNDEFINED_VARIABLE, n->line);
                            return n->nextName->acceptVisitor(this);
                        } else {
                            if (varaux->type->Class) {
                                this->setLastNameValues(n, NULL, 0);
                                n->tabLink = (IDSymbol*) varaux;
                                n->tabLinkType = VARIABLE_SYMBOL;
                                if (!this->inNew && !varaux->defined) ErrorMessage(UNDEFINED_VARIABLE, n->line);
                                return (int) varaux->type->Class;
                            } else {
                                this->setLastNameValues(n, NULL, 0);
                                n->tabLink = (IDSymbol*) varaux;
                                n->tabLinkType = VARIABLE_SYMBOL;
                                return varaux->type->primitiveType;
                            }
                        }
                    }
                }
                symbolAux = (IDSymbol*) symbolAux->getNext();
            }
            ParameterSymbol *paramaux = this->currentMethod->firstParam;
            while (paramaux) {
                if (n->nextName) {
                    this->setLastNameValues(n, (VariableSymbol*) paramaux->param, VARIABLE_SYMBOL);
                    n->tabLink = (IDSymbol*) paramaux;
                    n->tabLinkType = PARAMETER_SYMBOL;
                    return n->nextName->acceptVisitor(this);
                } else {
                    if (paramaux->param->type->Class) {
                        this->setLastNameValues(n, NULL, 0);
                        n->tabLink = (IDSymbol*) paramaux;
                        n->tabLinkType = PARAMETER_SYMBOL;
                        return (int) paramaux->param->type->Class;
                    } else {
                        this->setLastNameValues(n, NULL, 0);
                        n->tabLink = (IDSymbol*) paramaux;
                        n->tabLinkType = PARAMETER_SYMBOL;
                        return paramaux->param->type->primitiveType;
                    }
                }
                paramaux = paramaux->nextParam;
            }
            ErrorMessage(UNDECLARED_VAR, n->line);
            this->setLastNameValues(n, NULL, 0);
            return 0;
        }
    }
    this->setLastNameValues(n, NULL, 0);
    return 0;
}

void VisitorSemantic::setLastNameValues(node_Name* nameAssign, IDSymbol* auxLastName, int auxLastNameSType) {
    this->lastNameAssign = nameAssign;
    this->lastName = auxLastName;
    this->lastNameSymbolType = auxLastNameSType;
    return;
}

int VisitorSemantic::visit(node_New *n) {
    int type;

    if (n->type) n->type->acceptVisitor(this);
    //verifica se a expressao que define o tamanho do vetor e INT.
    if (n->arrayLengthExp) {
        type = n->arrayLengthExp->acceptVisitor(this);
        if (type == 0 || type != INTNUM) ErrorMessage(INCOMPATIBLE_TYPES, n->line);
    }

    return 0;
}

int VisitorSemantic::visit(node_Sign *s) {
    int resul;
    if (s->exp) resul = s->exp->acceptVisitor(this);

    if (resul == LOGICAL || resul == LITERAL || resul == INTVECTOR || resul == REALVECTOR) ErrorMessage(INCOMPATIBLE_TYPES, s->line);
    else return resul;
    return INTNUM;
}

int VisitorSemantic::visit(node_Not *n) {
    int resul;
    if (n->exp) resul = n->exp->acceptVisitor(this);

    if (resul != LOGICAL) ErrorMessage(INCOMPATIBLE_TYPES, n->line);
    return LOGICAL;
}

int VisitorSemantic::visit(node_BitwiseOP *b) {
    int left, right;
    if (b->leftExp) left = b->leftExp->acceptVisitor(this);
    if (b->rightExp) right = b->rightExp->acceptVisitor(this);

    if (left != LITERAL || left == right) return left;
    else ErrorMessage(INCOMPATIBLE_TYPES, b->line);

    return INTNUM;
}

int VisitorSemantic::visit(node_BooleanOP *b) {
    int left, right;
    if (b->leftExp) left = b->leftExp->acceptVisitor(this);
    if (b->rightExp) right = b->rightExp->acceptVisitor(this);

    if (left == LOGICAL || left == LITERAL || right == LOGICAL || right == LITERAL)
        ErrorMessage(INCOMPATIBLE_TYPES, b->line);
    else if (left == REALVECTOR || left == INTVECTOR || right == INTVECTOR || right == REALVECTOR)
        ErrorMessage(VECTOR_OPERATION, b->line);

    return LOGICAL;
}

int VisitorSemantic::visit(node_BinOp *b) {
    int left, right;
    if (b->leftExp) left = b->leftExp->acceptVisitor(this);
    if (b->rightExp) right = b->rightExp->acceptVisitor(this);

    if (left == LOGICAL || right == LOGICAL) {
        ErrorMessage(INCOMPATIBLE_TYPES, b->line);
        return INTNUM;
    } else if (left == INTVECTOR || left == REALVECTOR || right == INTVECTOR || right == REALVECTOR) {
        ErrorMessage(VECTOR_OPERATION, b->line);
        return INTNUM;
    } else {
        if (left == REALNUM || right == REALNUM) return REALNUM;
        else if (left == LITERAL || right == LITERAL) return LITERAL;
        else return INTNUM;
    }
}

int VisitorSemantic::visit(node_Bin2Op *b) {
    int left, right;
    if (b->leftExp) left = b->leftExp->acceptVisitor(this);
    if (b->rightExp) right = b->rightExp->acceptVisitor(this);
    if (left == LITERAL || left == LOGICAL || right == LITERAL || right == LOGICAL) {
        ErrorMessage(INCOMPATIBLE_TYPES, b->line);
        return INTNUM;
    } else if (left == INTVECTOR || left == REALVECTOR || right == REALVECTOR || right == INTVECTOR) {
        ErrorMessage(VECTOR_OPERATION, b->line);
        return INTNUM;
    } else {
        if (left == REALNUM || right == REALNUM) return REALNUM;
        else return INTNUM;
    }
}

int VisitorSemantic::visit(node_RelationalOP *r) {
    int left, right;
    if (r->leftExp) left = r->leftExp->acceptVisitor(this);
    if (r->rightExp) right = r->rightExp->acceptVisitor(this);
    switch (r->relOp) {
        case ANDLOGIC:
        case ORLOGIC:
            if (left == LOGICAL && right == LOGICAL) return LOGICAL;
            else ErrorMessage(INCOMPATIBLE_TYPES, r->line);
            return LOGICAL;

        case EQUAL:
        case NOTEQUAL:
            if (left == right && left != LITERAL && left != REALVECTOR && left != INTVECTOR) {
                return left;
            } else {
                ErrorMessage(INCOMPATIBLE_TYPES, r->line);
                return INTNUM;
            }
    }
    return 0;
}

int VisitorSemantic::visit(node_Number *n) {
    return n->type;
}

int VisitorSemantic::visit(node_Literal *l) {
    return LITERAL;
}

int VisitorSemantic::visit(node_Logic *l) {
    return LOGICAL;
}

/*********************************************************************
 *           Implementação da Classe VisitorIRTGen                   *
 *********************************************************************/
VisitorIRTGen::VisitorIRTGen() {
    this->fragmentList = new FragmentList(NULL, NULL);
    this->currentClassName = NULL;
    this->isArrayLenExp = false;
    this->thisPointer = NULL;
    this->scopeLevel = 0;
    this->isDestination = false;
}

VisitorIRTGen::~VisitorIRTGen() {
    if (this->fragmentList) delete this->fragmentList;
    if (this->mainMethod) delete this->mainMethod;
    if (this->mainMethodFrame) delete this->mainMethodFrame;
}

int VisitorIRTGen::visit(node_Program *p) {
    this->mainMethodFrame = new FrameMIPS();
    this->mainMethod = new Method((Frame*)this->mainMethodFrame, NULL);
    this->fragmentList->frag = (Fragment*) this->mainMethod;
    this->mainMethodFrame->label = new Label((char*) "main");
    if (p->classList) p->classList->acceptVisitor(this);
    return (int) this->fragmentList;
}

int VisitorIRTGen::visit(node_ClassList *cl) {
    this->currentClassName = cl->className;
    this->declType = ATTRIBUTE_SYMBOL;
    if (cl->firstDecl) cl->firstDecl->acceptVisitor(this);
    if (cl->nextClass) cl->nextClass->acceptVisitor(this);
    return 0;
}

int VisitorIRTGen::visit(node_VarList *vl) {
    if (this->declType == ATTRIBUTE_SYMBOL) {
        if (vl->nextDecl) return vl->nextDecl->acceptVisitor(this);
        else return 0;
    } else {
        if (vl->nextDecl) return (int) new IRNSeq((IRNStm*) vl->VarName->acceptVisitor(this), (IRNStm*) vl->nextDecl->acceptVisitor(this));
        else return vl->VarName->acceptVisitor(this);
    }
    return 0;
}

int VisitorIRTGen::visit(node_MethodList *ml) {
    Method* methodAux;
    bool isMainMethod = false;
    if ((strcmp(ml->methodName, (char*) "main") == 0) && (!ml->paramList) && (ml->returnType->primitiveType == VOID)) {
        methodAux = this->mainMethod;
        this->currentMethod = this->mainMethod;
        isMainMethod = true;
    } else {
        FrameMIPS* frameAux = new FrameMIPS();
        char lb[strlen(this->currentClassName) + strlen(ml->methodName) + 2];
        sprintf(lb, "%s$%s", this->currentClassName, ml->methodName);
        frameAux->label = new Label(lb);
        methodAux = new Method(frameAux, NULL);
        this->currentMethod = methodAux;
    }
    this->declType = PARAMETER_SYMBOL;

    if (ml->paramList) ml->paramList->acceptVisitor(this);

    this->declType = VARIABLE_SYMBOL;
    if (ml->stmtList)
        methodAux->body = new StmtFragmentList((StmtFragment*) ml->stmtList->acceptVisitor(this), NULL);

    if (!isMainMethod) {
        FragmentList* auxFragList = this->fragmentList;
        while (auxFragList->nextFrag) auxFragList = auxFragList->nextFrag;
        auxFragList->nextFrag = new FragmentList((Fragment*) methodAux, NULL);
    }
    this->declType = ATTRIBUTE_SYMBOL;
    if (ml->nextDecl) ml->nextDecl->acceptVisitor(this);
    return 0;
}

int VisitorIRTGen::visit(node_NameDecl *nd) {
    if (this->declType == ATTRIBUTE_SYMBOL) return 0;
    FrameMIPS *currentFrame = (FrameMIPS*)this->currentMethod->frame;
    if (this->declType == VARIABLE_SYMBOL) {
        if (nd->varType->isArray) {
            currentFrame->addLocal(true, 4, nd->varName, nd->varType->primitiveType);
        } else {
            if (nd->varType->className) {
                currentFrame->addLocal(true, 4, (char*) nd->varName, (int) nd->varType->className);
            } else {
                currentFrame->addLocal(false, 4, (char*) nd->varName, (int) nd->varType->primitiveType);
            }
        }
    } else {
        if (nd->varType->isArray) {
            currentFrame->addParam(true, 4, (char*) nd->varName, (int) nd->varType->primitiveType);
        } else {
            if (nd->varType->className) {
                currentFrame->addParam(true, 4, (char*) nd->varName, (int) nd->varType->className);
            } else {
                currentFrame->addParam(false, 4, (char*) nd->varName, (int) nd->varType->primitiveType);
            }
        }
    }
    Access_List *aux = currentFrame->access_list;
    InFrame *lastVarFrame = NULL;
    InReg *lastVarReg = NULL;
    while (aux->next) aux = aux->next;
    if (typeid (*aux->access) == typeid (InFrame)) lastVarFrame = (InFrame*) aux->access;
    else if (typeid (*aux->access) == typeid (InReg)) lastVarReg = (InReg*) aux->access;
    if (lastVarFrame) {
        if (this->scopeLevel > 0) {
            this->restrictScopeVars.push_back((Local_Access*) lastVarFrame);
            this->numVarDef[this->scopeLevel]++;
        }
        return (int) new IRNMove((IRNExp*) lastVarFrame->access_code(), (IRNExp*) new IRNConst(0));
    } else {
        if (lastVarReg) {
            if (this->scopeLevel > 0) {
                this->restrictScopeVars.push_back((Local_Access*) lastVarReg);
                this->numVarDef[this->scopeLevel]++;
            }
            return (int) new IRNMove((IRNExp*) lastVarReg->access_code(), (IRNExp*) new IRNConst(0));
        }
    }
    return 0;
}

int VisitorIRTGen::visit(node_Type *t) {
    return 0;
}

int VisitorIRTGen::visit(node_StmtList *sl) {
    if (sl->stmt) {
        IRNSeq *seq = new IRNSeq((IRNStm*) sl->stmt->acceptVisitor(this), NULL);
        if (sl->nextStmt) {
            seq->right = (IRNStm*) sl->nextStmt->acceptVisitor(this);
        }
        return (int) seq;
    }
    return (int) NULL;
}

int VisitorIRTGen::visit(node_While *w) {
    Label *teste = new Label();
    Label *inicio = new Label();
    Label *fim = new Label();
    this->scopeLevel++;
    IRNSeq *seq = new IRNSeq((IRNStm*)new IRNLabel(teste),
            new IRNSeq((IRNStm*)new IRNCJump(NOTEQUAL,
            (IRNExp*) w->condition->acceptVisitor(this),
            (IRNExp*) new IRNConst(0),
            inicio,
            fim),
            new IRNSeq((IRNStm*)new IRNLabel(inicio),
            (IRNStm*)new IRNSeq((IRNStm*) w->stmtList->acceptVisitor(this),
            (IRNStm*)new IRNSeq((IRNStm*)new IRNJump((IRNExp*) new IRNLabel(teste)),
            (IRNStm*)new IRNLabel(fim))))));
    for (int i = 0; i < this->numVarDef[this->scopeLevel]; i++) {
        this->restrictScopeVars.pop_back();
    }
    this->scopeLevel--;
    return (int) seq;
}

int VisitorIRTGen::visit(node_If *i) {
    Label *trueLabel = new Label();
    Label *falseLabel = new Label();
    Label *endLabel = new Label();

    //if com then e else
    if (i->stmtList && i->elseStmtList) {
        //testa a condição, se ela é verdadeira, vai pro trueLabel...
        this->scopeLevel++;
        IRNSeq *seq = new IRNSeq((IRNStm*) new IRNCJump(NOTEQUAL,
                (IRNExp*) i->condition->acceptVisitor(this),
                (IRNExp*) new IRNConst(0),
                trueLabel,
                falseLabel),
                (IRNStm*) new IRNSeq((IRNStm*) new IRNLabel(trueLabel),
                (IRNStm*) new IRNSeq((IRNStm*) i->stmtList->acceptVisitor(this),
                (IRNStm*)new IRNSeq((IRNStm*)new IRNJump((IRNExp*)new IRNLabel(endLabel)),
                (IRNStm*)new IRNSeq((IRNStm*)new IRNLabel(falseLabel),
                (IRNStm*)new IRNSeq((IRNStm*) i->elseStmtList->acceptVisitor(this),
                (IRNStm*) new IRNLabel(endLabel)))))));
        for (int i = 0; i < this->numVarDef[this->scopeLevel]; i++) {
            this->restrictScopeVars.pop_back();
        }
        this->scopeLevel--;
        return (int) seq;
    } else if (i->stmtList) { //if que nao tem else
        this->scopeLevel++;
        IRNSeq *seq = new IRNSeq((IRNStm*)new IRNCJump(NOTEQUAL,
                (IRNExp*) i->condition->acceptVisitor(this),
                (IRNExp*) new IRNConst(0),
                trueLabel,
                endLabel),
                (IRNStm*)new IRNSeq((IRNStm*) new IRNLabel(trueLabel),
                (IRNStm*) new IRNSeq((IRNStm*) i->stmtList->acceptVisitor(this),
                (IRNStm*)new IRNLabel(endLabel))));
        for (int i = 0; i < this->numVarDef[this->scopeLevel]; i++) {
            this->restrictScopeVars.pop_back();
        }
        this->scopeLevel--;
        return (int) seq;
    }
    return 0;
}

int VisitorIRTGen::visit(node_Return *r) {
    FrameMIPS *currentFrame = (FrameMIPS*)this->currentMethod->frame;
    return (int) new IRNSeq((IRNStm*)new IRNMove((IRNExp*)new IRNTemp(currentFrame->returnReg), (IRNExp*) r->exp->acceptVisitor(this)),
            (IRNStm*)new IRNJump((IRNExp*)new IRNName(currentFrame->returnAddress)));
}

int VisitorIRTGen::visit(node_Read *r) {
    return (int) new IRNExpStm((IRNExp*)new IRNCall((IRNExp*)new IRNName(new Label((char*) "SYSTEM_CALL_READ")), new IRNExpList((IRNExp*) r->exp->acceptVisitor(this), NULL)));
}

int VisitorIRTGen::visit(node_Println *p) {
    return (int) new IRNExpStm((IRNExp*)new IRNCall((IRNExp*)new IRNName(new Label((char*) "SYSTEM_CALL_PRINTLN")), (IRNExpList*) p->expList->acceptVisitor(this)));
}

int VisitorIRTGen::visit(node_Assign *a) {
    this->isDestination = true;
    IRNMove *move = new IRNMove((IRNExp*) a->Name->acceptVisitor(this), NULL);
    this->isDestination = false;
    move->source = (IRNExp*) a->assignmentExp->acceptVisitor(this);
    return (int) move;
}

int VisitorIRTGen::visit(node_Call *c) {
    return (int) new IRNExpStm((IRNExp*) c->callMethod->acceptVisitor(this));
}

int VisitorIRTGen::visit(node_ExpList *el) {
    if (el->nextExp)
        return (int) new IRNExpList((IRNExp*) el->exp->acceptVisitor(this), (IRNExpList*) el->nextExp->acceptVisitor(this));
    else
        return (int) new IRNExpList((IRNExp*) el->exp->acceptVisitor(this), NULL);
}

int VisitorIRTGen::visit(node_Name *n) {
    if (n->isThis) {
        this->thisPointer = (IRNExp*) new IRNMem((IRNExp*)new IRNBinop(PLUS, (IRNExp*)new IRNTemp(new Temp((char*) "$fp")), (IRNExp*)new IRNConst(0)));
        return (int) n->nextName->acceptVisitor(this);
    }
    if (n->isArray && !n->isMethod) {
        if (n->tabLinkType == ATTRIBUTE_SYMBOL) {
            AttributeSymbol *att = (AttributeSymbol*) n->tabLink, *auxAtt = NULL;
            ClassSymbol *aux = att->owner;
            int offset = 0;
            auxAtt = aux->firstAttribute;
            while (auxAtt) {
                if (auxAtt == att) break;
                else offset += 4;
                auxAtt = auxAtt->nextAttribute;
            }
            if (this->thisPointer) {
                IRNBinop *binop = new IRNBinop(PLUS, (IRNExp*)new IRNMem((IRNExp*)new IRNBinop(PLUS, (IRNExp*) this->thisPointer, (IRNExp*)new IRNConst(offset))), (IRNExp*)new IRNBinop(MULT, (IRNExp*)new IRNConst(4), (IRNExp*) new IRNBinop(PLUS, (IRNExp*) n->index->acceptVisitor(this), (IRNExp*)new IRNConst(1))));
                this->thisPointer = NULL;
                if (this->isDestination) return (int) binop;
                else return (int) new IRNMem((IRNExp*) binop);
            }
            if (this->isDestination) return (int) new IRNBinop(PLUS, (IRNExp*)new IRNMem((IRNExp*)new IRNBinop(PLUS, (IRNExp*) new IRNMem((IRNExp*)new IRNBinop(PLUS, (IRNExp*)new IRNTemp(new Temp((char*) "$fp")), (IRNExp*)new IRNConst(0))), (IRNExp*)new IRNConst(offset))), (IRNExp*)new IRNBinop(MULT, (IRNExp*)new IRNConst(4), (IRNExp*) new IRNBinop(PLUS, (IRNExp*) n->index->acceptVisitor(this), (IRNExp*)new IRNConst(1))));
            else return (int) new IRNMem((IRNExp*)new IRNBinop(PLUS, (IRNExp*)new IRNMem((IRNExp*)new IRNBinop(PLUS, (IRNExp*) new IRNMem((IRNExp*)new IRNBinop(PLUS, (IRNExp*)new IRNTemp(new Temp((char*) "$fp")), (IRNExp*)new IRNConst(0))), (IRNExp*)new IRNConst(offset))), (IRNExp*)new IRNBinop(MULT, (IRNExp*)new IRNConst(4), (IRNExp*) new IRNBinop(PLUS, (IRNExp*) n->index->acceptVisitor(this), (IRNExp*)new IRNConst(1)))));
        }
        if (n->tabLinkType == VARIABLE_SYMBOL) {
            bool found = false;
            InFrame *var = NULL;
            if (this->scopeLevel > 0) {
                if (!this->restrictScopeVars.empty())
                    for (int i = this->restrictScopeVars.size() - 1; i >= 0; i--) {
                        Local_Access *aux = this->restrictScopeVars[i];
                        if (typeid (*aux) == typeid (InFrame)) {
                            var = (InFrame*) aux;
                            if ((strcmp(var->name, n->name) == 0) && (!var->isParam)) {
                                found = true;
                                break;
                            } else var = NULL;
                        }
                    }
            }
            if (!found) {
                FrameMIPS *currentFrame = (FrameMIPS*) this->currentMethod->frame;
                Access_List *aux = currentFrame->access_list;
                while (aux) {
                    if (typeid (*aux->access) == typeid (InFrame)) {
                        var = (InFrame*) aux->access;
                        if ((strcmp(var->name, n->name) == 0) && (!var->isParam)) {
                            found = true;
                            break;
                        } else var = NULL;
                    }
                    aux = aux->next;
                }
            }
            this->thisPointer = NULL;
            if (this->isDestination) return (int) new IRNBinop(PLUS, (IRNExp*)new IRNMem((IRNExp*) var->access_code()), (IRNExp*)new IRNBinop(MULT, (IRNExp*)new IRNConst(4), (IRNExp*) new IRNBinop(PLUS, (IRNExp*) n->index->acceptVisitor(this), (IRNExp*)new IRNConst(1))));
            else return (int) new IRNMem((IRNExp*)new IRNBinop(PLUS, (IRNExp*)new IRNMem((IRNExp*) var->access_code()), (IRNExp*)new IRNBinop(MULT, (IRNExp*)new IRNConst(4), (IRNExp*) new IRNBinop(PLUS, (IRNExp*) n->index->acceptVisitor(this), (IRNExp*)new IRNConst(1)))));
        }
        if (n->tabLinkType == PARAMETER_SYMBOL) {
            FrameMIPS *currentFrame = (FrameMIPS*) this->currentMethod->frame;
            Access_List *aux = currentFrame->access_list;
            InFrame *varAccess = NULL;
            while (aux) {
                if (typeid (*aux->access) == typeid (InFrame)) {
                    varAccess = (InFrame*) aux->access;
                    if (varAccess->isParam && (strcmp(varAccess->name, n->name) == 0)) break;
                    else varAccess = NULL;
                }
                aux = aux->next;
            }
            if (varAccess) {
                this->thisPointer = NULL;
                if (this->isDestination) return (int) new IRNBinop(PLUS, (IRNExp*)new IRNMem((IRNExp*) varAccess->access_code()), (IRNExp*)new IRNBinop(MULT, (IRNExp*)new IRNConst(4), (IRNExp*) new IRNBinop(PLUS, (IRNExp*) n->index->acceptVisitor(this), (IRNExp*)new IRNConst(1))));
                else return (int) new IRNMem((IRNExp*)new IRNBinop(PLUS, (IRNExp*)new IRNMem((IRNExp*) varAccess->access_code()), (IRNExp*)new IRNBinop(MULT, (IRNExp*)new IRNConst(4), (IRNExp*) new IRNBinop(PLUS, (IRNExp*) n->index->acceptVisitor(this), (IRNExp*)new IRNConst(1)))));
            }
        }
    }
    if (n->isMethod && !n->isArray) {
        if (this->thisPointer) {
            IRNTemp *temp = new IRNTemp(new Temp());
            IRNExp *aux = this->thisPointer;
            IRNExpList *args = NULL;
            if (n->sentParamsExpList) args = (IRNExpList*) n->sentParamsExpList->acceptVisitor(this);
            this->thisPointer = aux;
            IRNExpList *expList = new IRNExpList((IRNExp*)this->thisPointer, (IRNExpList*) args);
            char methodName[256];
            if (n->tabLinkType == VARIABLE_SYMBOL) {
                VariableSymbol *var = (VariableSymbol*) n->tabLink;
                sprintf(methodName, "%s$%s", var->type->Class, n->name);
            } else {
                if (n->tabLinkType == ATTRIBUTE_SYMBOL) {
                    AttributeSymbol *att = (AttributeSymbol*) n->tabLink;
                    sprintf(methodName, "%s$%s", att->attribute->type->Class, n->name);
                } else {
                    MethodSymbol *meth = (MethodSymbol*) n->tabLink;
                    sprintf(methodName, "%s$%s", IDTable.lexemes + meth->owner->getLexemeIdx(), n->name);
                }
            }
            if (n->nextName) {
                this->thisPointer = (IRNExp*) new IRNEseq((IRNStm*)new IRNMove((IRNExp*) temp, (IRNExp*)new IRNCall((IRNExp*)new IRNName(new Label((char*) methodName)), expList)), (IRNExp*) temp);
                return (int) n->nextName->acceptVisitor(this);
            } else {
                this->thisPointer = NULL;
                return (int) new IRNEseq((IRNStm*)new IRNMove((IRNExp*) temp, (IRNExp*)new IRNCall((IRNExp*)new IRNName(new Label((char*) methodName)), expList)), (IRNExp*) temp);
            }
        } else {
            this->thisPointer = (IRNExp*) new IRNMem((IRNExp*)new IRNBinop(PLUS, (IRNExp*)new IRNTemp(new Temp((char*) "$fp")), (IRNExp*)new IRNConst(0)));
            IRNTemp *temp = new IRNTemp(new Temp());
            char methodName[256];
            IRNExpList *expList = new IRNExpList((IRNExp*)this->thisPointer, (IRNExpList*) n->sentParamsExpList->acceptVisitor(this));
            sprintf(methodName, "%s$%s", this->currentClassName, n->name);
            if (n->nextName) {
                this->thisPointer = (IRNExp*) new IRNEseq((IRNStm*)new IRNMove((IRNExp*) temp, (IRNExp*)new IRNCall((IRNExp*)new IRNName(new Label((char*) methodName)), expList)), (IRNExp*) temp);
                return (int) n->nextName->acceptVisitor(this);
            } else {
                this->thisPointer = NULL;
                return (int) new IRNEseq((IRNStm*)new IRNMove((IRNExp*) temp, (IRNExp*)new IRNCall((IRNExp*)new IRNName(new Label((char*) methodName)), expList)), (IRNExp*) temp);
            }
        }
    }
    if (n->isArray && n->isMethod) {
        if (!this->thisPointer) this->thisPointer = (IRNExp*) new IRNMem((IRNExp*)new IRNBinop(PLUS, (IRNExp*)new IRNTemp(new Temp((char*) "$fp")), (IRNExp*)new IRNConst(0)));
        IRNTemp *temp = new IRNTemp(new Temp());
        IRNExpList *expList = new IRNExpList((IRNExp*)this->thisPointer, (IRNExpList*) n->sentParamsExpList->acceptVisitor(this));
        char methodName[256];
        MethodSymbol *meth = NULL;
        if (n->tabLinkType == METHOD_SYMBOL) meth = (MethodSymbol*) n->tabLink;
        sprintf(methodName, "%s$%s", IDTable.lexemes + meth->owner->getLexemeIdx(), n->name);
        IRNEseq *eseq = new IRNEseq((IRNStm*)new IRNMove((IRNExp*) temp, (IRNExp*)new IRNCall((IRNExp*)new IRNName(new Label((char*) methodName)), expList)), (IRNExp*) temp);
        this->thisPointer = NULL;
        if (this->isDestination) return (int) new IRNBinop(PLUS, (IRNExp*) eseq, (IRNExp*)new IRNBinop(MULT, (IRNExp*)new IRNConst(4), (IRNExp*) new IRNBinop(PLUS, (IRNExp*) n->index->acceptVisitor(this), (IRNExp*)new IRNConst(1))));
        else return (int) new IRNMem((IRNExp*)new IRNBinop(PLUS, (IRNExp*) eseq, (IRNExp*)new IRNBinop(MULT, (IRNExp*)new IRNConst(4), (IRNExp*) new IRNBinop(PLUS, (IRNExp*) n->index->acceptVisitor(this), (IRNExp*)new IRNConst(1)))));
    }
    if (n->tabLinkType == ATTRIBUTE_SYMBOL) {
        IRNBinop *binop = NULL;
        AttributeSymbol *att = (AttributeSymbol*) n->tabLink;
        att = att->owner->firstAttribute;
        int offset = 0;
        while (att) {
            if (strcmp(IDTable.lexemes + att->getLexemeIdx(), n->name) == 0) break;
            else offset += 4;
            att = att->nextAttribute;
        }
        if (n->nextName) {
            if (this->thisPointer) {
                binop = new IRNBinop(PLUS, (IRNExp*)new IRNConst(offset), this->thisPointer);
                this->thisPointer = (IRNExp*) new IRNMem((IRNExp*) binop);
                return (int) n->nextName->acceptVisitor(this);
            } else {
                binop = new IRNBinop(PLUS, (IRNExp*)new IRNConst(offset), (IRNExp*)new IRNBinop(PLUS, (IRNExp*)new IRNTemp(new Temp((char*) "$fp")), (IRNExp*)new IRNConst(0)));
                this->thisPointer = (IRNExp*) new IRNMem((IRNExp*) binop);
                return (int) n->nextName->acceptVisitor(this);
            }
        } else {
            if (this->thisPointer) {
                binop = new IRNBinop(PLUS, (IRNExp*)new IRNConst(offset), this->thisPointer);
                this->thisPointer = NULL;
                if (this->isDestination) return (int) binop;
                else return (int) new IRNMem((IRNExp*) binop);
            } else {
                binop = new IRNBinop(PLUS, (IRNExp*)new IRNConst(offset), (IRNExp*)new IRNBinop(PLUS, (IRNExp*)new IRNTemp(new Temp((char*) "$fp")), (IRNExp*)new IRNConst(0)));
                this->thisPointer = NULL;
                if (this->isDestination) return (int) binop;
                else return (int) new IRNMem((IRNExp*) binop);
            }
        }
    }
    if (n->tabLinkType == VARIABLE_SYMBOL || n->tabLinkType == PARAMETER_SYMBOL) {
        bool found = false;
        InFrame *varFrame = NULL;
        InReg *varReg = NULL;
        if (this->scopeLevel > 0) {
            if (!this->restrictScopeVars.empty())
                for (int i = this->restrictScopeVars.size() - 1; i >= 0; i--) {
                    Local_Access *aux = this->restrictScopeVars[i];
                    if (typeid (*aux) == typeid (InFrame)) {
                        InFrame *varaux = (InFrame*) aux;
                        if ((strcmp(varaux->name, n->name) == 0)) {
                            found = true;
                            varFrame = varaux;
                            break;
                        }
                    } else {
                        if (typeid (*aux) == typeid (InReg)) {
                            InReg *varaux = (InReg*) aux;
                            if ((strcmp(varaux->name, n->name) == 0)) {
                                found = true;
                                varReg = varaux;
                                break;
                            }
                        }
                    }
                }
        }
        if (!found) {
            FrameMIPS *currentFrame = (FrameMIPS*) this->currentMethod->frame;
            Access_List *aux = currentFrame->access_list;
            while (aux) {
                if (typeid (*aux->access) == typeid (InFrame)) {
                    InFrame *varaux = (InFrame*) aux->access;
                    if ((strcmp(varaux->name, n->name) == 0)) {
                        found = true;
                        varFrame = varaux;
                        break;
                    }
                } else {
                    if (typeid (*aux->access) == typeid (InReg)) {
                        InReg *varaux = (InReg*) aux->access;
                        if ((strcmp(varaux->name, n->name) == 0)) {
                            found = true;
                            varReg = varaux;
                            break;
                        }
                    }
                }
                aux = aux->next;
            }
        }
        if (n->nextName) {
            if (varFrame) {
                this->thisPointer = (IRNExp*) new IRNMem((IRNExp*) varFrame->access_code());
            }
            return (int) n->nextName->acceptVisitor(this);
        } else {
            if (varFrame) {
                this->thisPointer = NULL;
                if (this->isDestination) return (int) varFrame->access_code();
                else return (int) new IRNMem((IRNExp*) varFrame->access_code());
            } else
                if (varReg) {
                this->thisPointer = NULL;
                return (int) varReg->access_code();
            }
            return (int) n->nextName->acceptVisitor(this);
        }
    }
    if (n->length) {
        IRNBinop *binop = new IRNBinop(PLUS, (IRNExp*)this->thisPointer, (IRNExp*)new IRNConst(0));
        this->thisPointer = NULL;
        return (int) new IRNMem((IRNExp*) binop);
    }
    return 0;
}

int VisitorIRTGen::visit(node_New *n) {

    if (n->type->className) {
        //alocar objeto no heap(todos os atributos da classe e das estendidas)
        ClassSymbol* classAux = (ClassSymbol*) IDTable.searchID(n->type->className, CLASS_SYMBOL);
        AttributeSymbol* attAux;
        if (classAux->extendedClass) {
            while (classAux) {
                this->classHierarchy.push_back(classAux);
                classAux = classAux->extendedClass;
            }
            int numAtts = 0;
            IRNTemp *tmp = new IRNTemp(new Temp());
            IRNSeq *seq = new IRNSeq(NULL, NULL);
            IRNSeq *aux = seq;
            while (!this->classHierarchy.empty()) {
                classAux = (ClassSymbol*)this->classHierarchy.back();
                this->classHierarchy.pop_back();
                attAux = classAux->firstAttribute;
                while (attAux) {
                    IRNSeq *peace = new IRNSeq((IRNStm*)new IRNMove((IRNExp*)new IRNMem((IRNExp*)new IRNBinop(PLUS, (IRNExp*) tmp, (IRNExp*)new IRNConst(numAtts * 4))), (IRNExp*)new IRNConst(0)), NULL);
                    aux->right = peace;
                    aux = peace;
                    numAtts++;
                    attAux = attAux->nextAttribute;
                }
            }
            seq->left = (IRNStm*)new IRNMove((IRNExp*) tmp, (IRNExp*)new IRNCall((IRNExp*)new IRNName(new Label((char*) "SYSTEM_CALL_INITOBJECT")), new IRNExpList((IRNExp*)new IRNConst(numAtts * 4), NULL)));
            return (int) new IRNEseq((IRNStm*) seq, (IRNExp*) tmp);
        } else {
            attAux = classAux->firstAttribute;
            int numAtts = 0;
            IRNTemp *tmp = new IRNTemp(new Temp());
            IRNSeq *seq = new IRNSeq(NULL, NULL);
            IRNSeq *aux = seq;
            while (attAux) {
                IRNSeq *peace = new IRNSeq((IRNStm*)new IRNMove((IRNExp*)new IRNMem((IRNExp*)new IRNBinop(PLUS, (IRNExp*) tmp, (IRNExp*)new IRNConst(numAtts * 4))), (IRNExp*)new IRNConst(0)), NULL);
                aux->right = peace;
                aux = peace;
                numAtts++;
                attAux = attAux->nextAttribute;
            }
            seq->left = (IRNStm*)new IRNMove((IRNExp*) tmp, (IRNExp*)new IRNCall((IRNExp*)new IRNName(new Label((char*) "SYSTEM_CALL_INITOBJECT")), new IRNExpList((IRNExp*)new IRNConst(numAtts * 4), NULL)));
            return (int) new IRNEseq((IRNStm*) seq, (IRNExp*) tmp);
        }
    } else {
        //alocar array no heap
        this->isArrayLenExp = true;
        int arrayLen = n->arrayLengthExp->acceptVisitor(this);
        this->isArrayLenExp = false;
        IRNTemp *tmp = new IRNTemp(new Temp());
        IRNSeq *seq1 = new IRNSeq((IRNStm*)new IRNMove((IRNExp*) tmp, (IRNExp*)new IRNCall((IRNExp*)new IRNName(new Label((char*) "SYSTEM_CALL_INITARRAY")), new IRNExpList((IRNExp*)new IRNConst((arrayLen + 1) * 4), NULL))), (IRNStm*)new IRNMove((IRNExp*)new IRNMem((IRNExp*)new IRNBinop(PLUS, (IRNExp*) tmp, (IRNExp*)new IRNConst(0))), (IRNExp*)new IRNConst(arrayLen)));
        IRNSeq *seq = new IRNSeq((IRNStm*) seq1, NULL);
        IRNSeq *aux = seq;
        for (int i = 1; i <= arrayLen; i++) {
            IRNSeq *peace = new IRNSeq((IRNStm*)new IRNMove((IRNExp*)new IRNMem((IRNExp*)new IRNBinop(PLUS, (IRNExp*) tmp, (IRNExp*)new IRNConst(i * 4))), (IRNExp*)new IRNConst(0)), NULL);
            aux->right = peace;
            aux = peace;
        }
        return (int) new IRNEseq((IRNStm*) seq, (IRNExp*) tmp);
    }

    return 0;
}

int VisitorIRTGen::visit(node_Sign *s) {
    return (int) new IRNBinop(s->sign, new IRNConst(0), (IRNExp*) s->exp->acceptVisitor(this));
}

int VisitorIRTGen::visit(node_Not *n) {
    Label *l1 = new Label();
    Label *l2 = new Label();
    Temp *t = new Temp();

    IRNCJump *cjump = new IRNCJump(EQUAL, (IRNExp*) n->exp->acceptVisitor(this), (IRNExp*)new IRNConst(0), l1, l2);

    IRNMove *move = new IRNMove((IRNExp*)new IRNTemp(t), (IRNExp*)new IRNConst(0));

    IRNEseq *eseq = new IRNEseq((IRNStm*)new IRNSeq((IRNStm*) cjump,
            (IRNStm*)new IRNSeq((IRNStm*)new IRNLabel(l2),
            (IRNStm*)new IRNSeq((IRNStm*) move,
            (IRNStm*)new IRNLabel(l1)
            )
            )
            ),
            (IRNExp*)new IRNTemp(t)
            );
    return (int) eseq;
}

int VisitorIRTGen::visit(node_BitwiseOP *b) {
    return (int) new IRNBinop(b->bitOp, (IRNExp*) b->leftExp->acceptVisitor(this), (IRNExp*) b->rightExp->acceptVisitor(this));
}

int VisitorIRTGen::visit(node_BooleanOP *b) {
    Temp *temp = new Temp();
    Label *l1 = new Label();
    Label *l2 = new Label();
    IRNEseq *eseq = new IRNEseq((IRNStm*) new IRNSeq((IRNStm*)new IRNMove((IRNExp*)new IRNTemp(temp),
            (IRNExp*)new IRNConst(1)),
            (IRNStm*)new IRNSeq((IRNStm*)new IRNCJump(b->boolOp,
            (IRNExp*) b->leftExp->acceptVisitor(this),
            (IRNExp*) b->rightExp->acceptVisitor(this),
            l1,
            l2),
            (IRNStm*)new IRNSeq((IRNStm*)new IRNLabel(l2),
            (IRNStm*)new IRNSeq((IRNStm*)new IRNMove((IRNExp*)new IRNTemp(temp),
            (IRNExp*) new IRNConst(0)),
            (IRNStm*)new IRNLabel(l1))))),
            (IRNExp*)new IRNTemp(temp));

    return (int) eseq;
}

int VisitorIRTGen::visit(node_BinOp *b) {
    return (int) new IRNBinop(b->binOp, (IRNExp*) b->leftExp->acceptVisitor(this), (IRNExp*) b->rightExp->acceptVisitor(this));
}

int VisitorIRTGen::visit(node_Bin2Op *b) {
    return (int) new IRNBinop(b->bin2Op, (IRNExp*) b->leftExp->acceptVisitor(this), (IRNExp*) b->rightExp->acceptVisitor(this));
}

int VisitorIRTGen::visit(node_RelationalOP *r) {
    Temp *t = new Temp();
    Label *l1 = new Label();
    Label *l2 = new Label();
    Label *l3 = new Label();
    IRNCJump *cjump1;
    IRNCJump *cjump2;
    IRNMove *move1;
    IRNMove *move2;
    IRNEseq* eseq;

    switch (r->relOp) {
        case EQUAL:
        case NOTEQUAL:
            cjump1 = new IRNCJump(r->relOp, (IRNExp*) r->leftExp->acceptVisitor(this), (IRNExp*) r->rightExp->acceptVisitor(this), l1, l2);
            eseq = new IRNEseq((IRNStm*) new IRNSeq((IRNStm*)new IRNMove((IRNExp*)new IRNTemp(t),
                    (IRNExp*)new IRNConst(1)),
                    (IRNStm*)new IRNSeq((IRNStm*) cjump1,
                    (IRNStm*)new IRNSeq((IRNStm*)new IRNLabel(l2),
                    (IRNStm*)new IRNSeq((IRNStm*)new IRNMove((IRNExp*)new IRNTemp(t),
                    (IRNExp*) new IRNConst(0)),
                    (IRNStm*)new IRNLabel(l1))))),
                    (IRNExp*)new IRNTemp(t));
            return (int) eseq;

        case ORLOGIC:
            move1 = new IRNMove((IRNExp*)new IRNTemp(t), (IRNExp*) r->leftExp->acceptVisitor(this));
            move2 = new IRNMove((IRNExp*)new IRNTemp(t), (IRNExp*)new IRNConst(1));

            cjump1 = new IRNCJump(NOTEQUAL, (IRNExp*) r->rightExp->acceptVisitor(this), (IRNExp*)new IRNConst(0), l1, l2);

            eseq = new IRNEseq(new IRNSeq((IRNStm*) move1,
                    (IRNStm*)new IRNSeq((IRNStm*) cjump1,
                    (IRNStm*)new IRNSeq((IRNStm*)new IRNLabel(l1),
                    (IRNStm*)new IRNSeq((IRNStm*) move2,
                    (IRNStm*)new IRNLabel(l2)
                    )
                    )
                    )

                    ),
                    new IRNTemp(t)
                    );
            return (int) eseq;

        case ANDLOGIC:
            cjump1 = new IRNCJump(NOTEQUAL, (IRNExp*) r->leftExp->acceptVisitor(this), new IRNConst(0), l1, l2);
            cjump2 = new IRNCJump(NOTEQUAL, (IRNExp*) r->rightExp->acceptVisitor(this), new IRNConst(0), l3, l2);

            IRNMove *moveAux = new IRNMove((IRNExp*)new IRNTemp(t), (IRNExp*)new IRNConst(0));

            IRNEseq *eseq = new IRNEseq(NULL, (IRNExp*)new IRNTemp(t));
            eseq->stm = (IRNStm*)new IRNSeq((IRNStm*)new IRNMove((IRNExp*)new IRNTemp(t),
                    (IRNExp*)new IRNConst(0)),
                    (IRNStm*)new IRNSeq((IRNStm*) cjump1,
                    (IRNStm*)new IRNSeq((IRNStm*)new IRNLabel(l1),
                    (IRNStm*)new IRNSeq((IRNStm*) cjump2,
                    (IRNStm*)new IRNSeq((IRNStm*)new IRNLabel(l3),
                    (IRNStm*)new IRNSeq((IRNStm*) moveAux,
                    (IRNStm*)new IRNLabel(l2)
                    )
                    )
                    )

                    )

                    )
                    );
            return (int) eseq;
            break;
    }
    return 0;
}

int VisitorIRTGen::visit(node_Number *n) {
    switch (n->type) {
        case INTNUM:
            if (this->isArrayLenExp) return atoi(n->number);
            return (int) new IRNConst(atoi(n->number));
        case REALNUM:
            return (int) new IRNConstf(atof(n->number));
    }
    return 0;
}

int VisitorIRTGen::visit(node_Literal *l) {
    Literal_Frag *litFrag = new Literal_Frag(l->literal);
    FragmentList* auxFragList = this->fragmentList;
    while (auxFragList->nextFrag) auxFragList = auxFragList->nextFrag;
    auxFragList->nextFrag = new FragmentList((Fragment*) litFrag, NULL);
    return (int) new IRNLabel(litFrag->label_literal);
}

int VisitorIRTGen::visit(node_Logic *l) {
    if (l->logic == TRUE) return (int) new IRNConst(1);
    else return (int) new IRNConst(0);
}

/*********************************************************************
 *          Implementação da Classe VisitorIRTPrint                  *
 *********************************************************************/

VisitorIRTPrint::VisitorIRTPrint() {
    this->tab = 0;
}

VisitorIRTPrint::~VisitorIRTPrint() {

}

void VisitorIRTPrint::putTab() {
    fprintf(stdout, "\n");
    for (int i = 0; i < this->tab; i++) fprintf(stdout, "|    ");
}

int VisitorIRTPrint::visit(StmtFragmentList *sfl) {
    tab++;
    putTab();
    fprintf(stdout, "BODY:");
    tab++;
    if (sfl->stmFrag) sfl->stmFrag->acceptVisitor(this);
    if (sfl->nextStmFrag) sfl->nextStmFrag->acceptVisitor(this);
    tab--;
    tab--;
    return 0;
}

int VisitorIRTPrint::visit(Access_List *al) {
    return 0;
}

int VisitorIRTPrint::visit(Label *l) {
    putTab();
    if (l->Label_Name) fprintf(stdout, "%s", l->Label_Name);
    return 0;
}

int VisitorIRTPrint::visit(Temp *t) {
    putTab();
    if (t->name) fprintf(stdout, "%s", t->name);
    return 0;
}

int VisitorIRTPrint::visit(Label_List *ll) {
    if (ll->label) ll->label->acceptVisitor(this);
    if (ll->next) ll->next->acceptVisitor(this);
    return 0;
}

int VisitorIRTPrint::visit(Temp_List *tl) {
    if (tl->temp) tl->temp->acceptVisitor(this);
    if (tl->next) tl->next->acceptVisitor(this);
    return 0;
}

int VisitorIRTPrint::visit(FragmentList *fl) {
    if (!fl) return 0;
    fprintf(stdout, "\n\n+==========================================================================+\n");
    fprintf(stdout, "|                    ARVORE DE REPRESENTACAO INTERMEDIARIA                 |\n");
    fprintf(stdout, "+==========================================================================+\n\n");

    FragmentList *aux;
    aux = fl->nextFrag;

    if (fl->frag) fl->frag->acceptVisitor(this);

    while (aux) {
        aux->frag->acceptVisitor(this);
        aux = aux->nextFrag;
    }

    fprintf(stdout, "\n\n+==================== ARI CONSTRUIDA COM SUCESSO! =========================+\n\n");
    return 0;
}

int VisitorIRTPrint::visit(Method *m) {
    tab++;
    putTab();
    fprintf(stdout, "METHOD:");
    if (m->frame) m->frame->acceptVisitor(this);
    if (m->body) m->body->acceptVisitor(this);
    tab--;
    return 0;
}

int VisitorIRTPrint::visit(Literal_Frag *lf) {
    tab++;
    putTab();
    fprintf(stdout, "LITERAL:");
    tab++;
    if (lf->label_literal) lf->label_literal->acceptVisitor(this);
    tab--;
    tab--;
    return 0;
}

int VisitorIRTPrint::visit(InFrame *i) {
    return 0;
}

int VisitorIRTPrint::visit(InReg *i) {
    return 0;
}

int VisitorIRTPrint::visit(FrameMIPS *f) {
    tab++;
    putTab();
    fprintf(stdout, "FRAME:");
    tab++;
    f->label->acceptVisitor(this);
    putTab();
    fprintf(stdout, "- Numero de parametros: %i", f->number_of_params);
    putTab();
    fprintf(stdout, "- Tamanho dos parametros: %i", f->param_size);
    tab--;
    tab--;
    return 0;
}

int VisitorIRTPrint::visit(IRNStmList *sl) {
    if (sl->stm) sl->stm->acceptVisitor(this);
    if (sl->tail) sl->tail->acceptVisitor(this);
    return 0;
}

int VisitorIRTPrint::visit(IRNExpList *el) {
    if (el->head) el->head->acceptVisitor(this);
    if (el->tail) el->head->acceptVisitor(this);
    return 0;
}

int VisitorIRTPrint::visit(IRNConst *c) {
    putTab();
    fprintf(stdout, "CONST: %d", c->value);
    return 0;
}

int VisitorIRTPrint::visit(IRNConstf *c) {
    putTab();
    fprintf(stdout, "CONSTF: %f", c->value);
    return 0;
}

int VisitorIRTPrint::visit(IRNName *n) {
    putTab();
    fprintf(stdout, "NAME:");
    tab++;
    if (n->label) n->label->acceptVisitor(this);
    tab--;
    return 0;
}

int VisitorIRTPrint::visit(IRNTemp *t) {
    putTab();
    fprintf(stdout, "TEMP:");
    tab++;
    if (t->temp) t->temp->acceptVisitor(this);
    tab--;
    return 0;
}

int VisitorIRTPrint::visit(IRNBinop *b) {
    putTab();
    fprintf(stdout, "BINOP:");
    tab++;
    putTab();
    fprintf(stdout, "OP: ");
    printToken(b->binop);
    if (b->left) b->left->acceptVisitor(this);
    if (b->right) b->right->acceptVisitor(this);
    tab--;
    return 0;
}

int VisitorIRTPrint::visit(IRNMem *m) {
    putTab();
    fprintf(stdout, "MEM:");
    if (m->exp) m->exp->acceptVisitor(this);
    return 0;
}

int VisitorIRTPrint::visit(IRNCall *el) {
    putTab();
    fprintf(stdout, "CALL:");
    tab++;
    if (el->func) el->func->acceptVisitor(this);
    if (el->args) el->args->acceptVisitor(this);
    tab--;
    return 0;
}

int VisitorIRTPrint::visit(IRNEseq *c) {
    putTab();
    fprintf(stdout, "ESEQ:");
    tab++;
    if (c->stm) c->stm->acceptVisitor(this);
    if (c->exp) c->exp->acceptVisitor(this);
    tab--;
    return 0;
}

int VisitorIRTPrint::visit(IRNMove *n) {
    putTab();
    fprintf(stdout, "MOVE:");
    tab++;
    if (n->destination) n->destination->acceptVisitor(this);
    if (n->source) n->source->acceptVisitor(this);
    tab--;
    return 0;
}

int VisitorIRTPrint::visit(IRNExpStm *es) {
    putTab();
    if (es->exp) es->exp->acceptVisitor(this);
    return 0;
}

int VisitorIRTPrint::visit(IRNJump *j) {
    putTab();
    fprintf(stdout, "JUMP:");
    tab++;
    if (j->exp) j->exp->acceptVisitor(this);
    tab--;
    return 0;
}

int VisitorIRTPrint::visit(IRNCJump *cj) {
    putTab();
    fprintf(stdout, "CJUMP:");
    tab++;
    putTab();
    fprintf(stdout, "OP: ");
    printToken(cj->relOp);
    if (cj->left) cj->left->acceptVisitor(this);
    if (cj->right) cj->right->acceptVisitor(this);
    if (cj->iftrue) cj->iftrue->acceptVisitor(this);
    if (cj->iffalse) cj->iffalse->acceptVisitor(this);
    tab--;
    return 0;
}

int VisitorIRTPrint::visit(IRNSeq *s) {
    putTab();
    fprintf(stdout, "SEQ:");
    tab++;
    if (s->left) s->left->acceptVisitor(this);
    if (s->right) s->right->acceptVisitor(this);
    tab--;
    return 0;
}

int VisitorIRTPrint::visit(IRNLabel *l) {
    putTab();
    fprintf(stdout, "LABEL:");
    tab++;
    if (l->label) l->label->acceptVisitor(this);
    tab--;
    return 0;
}

/*********************************************************************
 *          Implementação da Classe VisitorIRTCanonizer              *
 *********************************************************************/

VisitorIRTCanonizer::VisitorIRTCanonizer() {
    this->changed = true;
}

void VisitorIRTCanonizer::visit(StmtFragmentList *sfl) {
    if (sfl->stmFrag) sfl->stmFrag->acceptVisitor(this);
    if (sfl->nextStmFrag) sfl->nextStmFrag->acceptVisitor(this);

}

void VisitorIRTCanonizer::visit(Access_List *al) {

}

void VisitorIRTCanonizer::visit(Label *l) {

}

void VisitorIRTCanonizer::visit(Temp *t) {

}

void VisitorIRTCanonizer::visit(Label_List *ll) {
    if (ll->label) ll->label->acceptVisitor(this);
    if (ll->next) ll->next->acceptVisitor(this);
}

void VisitorIRTCanonizer::visit(Temp_List *tl) {
    if (tl->next) tl->next->acceptVisitor(this);
    if (tl->temp) tl->temp->acceptVisitor(this);
}

void VisitorIRTCanonizer::visit(FragmentList *fl) {
    this->changed = false;
    if (fl->frag) fl->frag->acceptVisitor(this);
    FragmentList *listAux;
    listAux = fl->nextFrag;
    while (listAux) {
        listAux->frag->acceptVisitor(this);
        listAux = listAux->nextFrag;
    }
}

void VisitorIRTCanonizer::visit(Method *m) {
    if (m->body) m->body->acceptVisitor(this);
    if (m->frame) m->frame->acceptVisitor(this);
}

void VisitorIRTCanonizer::visit(Literal_Frag *lf) {
    if (lf->label_literal) lf->label_literal->acceptVisitor(this);
}

void VisitorIRTCanonizer::visit(InFrame *i) {
}

void VisitorIRTCanonizer::visit(InReg *i) {
    if (i->temp) i->temp->acceptVisitor(this);
}

void VisitorIRTCanonizer::visit(FrameMIPS *f) {
    if (f->access_list) f->access_list->acceptVisitor(this);
    if (f->label) f->label->acceptVisitor(this);
    if (f->returnReg) f->returnReg->acceptVisitor(this);
    if (f->tempList) f->tempList->acceptVisitor(this);
}

void VisitorIRTCanonizer::visit(IRNStmList *sl) {
    if (sl->stm) sl->stm->acceptVisitor(this);
    if (sl->tail) sl->tail->acceptVisitor(this);
}

void VisitorIRTCanonizer::visit(IRNExpList *el) {
    if (el->head) el->head->acceptVisitor(this);
    if (el->tail) el->tail->acceptVisitor(this);
}

IRNExp* VisitorIRTCanonizer::visit(IRNConst *c) {
    return c;
}

IRNExp* VisitorIRTCanonizer::visit(IRNConstf *c) {
    return c;
}

IRNExp* VisitorIRTCanonizer::visit(IRNName *n) {
    if (n->label) n->label->acceptVisitor(this);
    return n;
}

IRNExp* VisitorIRTCanonizer::visit(IRNTemp *t) {
    if (t->temp) t->temp->acceptVisitor(this);
    return t;
}

IRNExp* VisitorIRTCanonizer::visit(IRNBinop *b) {
    if (b->left) b->left = b->left->acceptVisitor(this);
    if (b->right) b->right = b->right->acceptVisitor(this);
    if (b->left && (typeid (*(b->left)) == typeid (IRNEseq))) {
        this->changed = true;
        IRNEseq* eseqAux = (IRNEseq*) b->left;
        return (IRNExp*) (new IRNEseq(eseqAux->stm, (IRNExp*) (new IRNBinop(b->binop, eseqAux->exp, b->right))));
    }

    if (b->right && (typeid (*(b->right)) == typeid (IRNEseq))) {
        this->changed = true;
        IRNEseq* eseqAux = (IRNEseq*) b->right;
        Temp* t = new Temp();
        IRNMove* moveAux = new IRNMove((IRNExp*) (new IRNTemp(t)), b->left);
        IRNBinop* binopAux = new IRNBinop(b->binop, (IRNExp*) (new IRNTemp(t)), eseqAux->exp);
        return (IRNExp*) (new IRNEseq((IRNStm*) moveAux, (IRNExp*) (new IRNEseq(eseqAux->stm, (IRNExp*) binopAux))));
    }
    return (IRNExp*) b;
}

IRNExp* VisitorIRTCanonizer::visit(IRNMem *m) {
    if (m->exp) m->exp = m->exp->acceptVisitor(this);

    if (typeid (*m->exp) == typeid (IRNEseq)) {
        this->changed = true;
        IRNEseq* eseqAux = (IRNEseq*) m->exp;
        return (IRNExp*) (new IRNEseq(eseqAux->stm, (IRNExp*) (new IRNMem(eseqAux->exp))));
    }

    return (IRNExp*) m;
}

IRNExp* VisitorIRTCanonizer::visit(IRNCall *el) {
    if (el->func) el->func = el->func->acceptVisitor(this);
    return (IRNExp*) el;
}

IRNExp* VisitorIRTCanonizer::visit(IRNEseq *c) {
    if (c->stm) c->stm->acceptVisitor(this);

    if (c->exp) {
        IRNExp* expAux = c->exp->acceptVisitor(this);
        if (typeid (*expAux) == typeid (IRNEseq)) {
            this->changed = true;
            IRNEseq* eseqAux = (IRNEseq*) expAux;
            return (IRNExp*) (new IRNEseq(new IRNSeq(c->stm, eseqAux->stm), eseqAux->exp));
        }
    }

    return (IRNExp*) c;
}

IRNStm* VisitorIRTCanonizer::visit(IRNMove *n) {
    IRNExp* sourceAux;
    IRNExp* destinationAux;

    if (n->source) sourceAux = n->source->acceptVisitor(this);
    if (n->destination) destinationAux = n->destination->acceptVisitor(this);
    if (typeid (*destinationAux) == typeid (IRNEseq)) {
        this->changed = true;
        IRNEseq* eseqAux = (IRNEseq*) destinationAux;
        IRNMove* moveAux = new IRNMove((IRNExp*) eseqAux->exp, (IRNExp*) sourceAux);
        return (IRNStm*) new IRNSeq((IRNStm*) eseqAux->stm, (IRNStm*) moveAux);
    }

    if (typeid (*sourceAux) == typeid (IRNEseq)) {
        this->changed = true;
        IRNEseq* eseqAux = (IRNEseq*) sourceAux;
        IRNMove* moveAux = new IRNMove((IRNExp*) eseqAux->exp, (IRNExp*) destinationAux);
        return (IRNStm*) new IRNSeq((IRNStm*) eseqAux->stm, (IRNStm*) moveAux);
    }

    return (IRNStm*) new IRNMove((IRNExp*) destinationAux, (IRNExp*) sourceAux);
}

IRNStm* VisitorIRTCanonizer::visit(IRNExpStm *es) {
    if (es) es->exp = es->exp->acceptVisitor(this);
    return (IRNStm*) es;
}

IRNStm* VisitorIRTCanonizer::visit(IRNJump *j) {
    if (j->exp) j->exp = (IRNExp*) j->exp->acceptVisitor(this);

    if (j->exp && (typeid (*j->exp) == typeid (IRNEseq))) {
        this->changed = true;
        IRNEseq* eseqAux = (IRNEseq*) j->exp;
        return (IRNStm*) (new IRNSeq(eseqAux->stm, (IRNStm*) (new IRNJump(eseqAux->exp))));
    }

    return (IRNStm*) j;
}

IRNStm* VisitorIRTCanonizer::visit(IRNCJump *cj) {
    if (cj->left) cj->left = cj->left->acceptVisitor(this);
    if (cj->right) cj->left = cj->right->acceptVisitor(this);

    if (cj->left && (typeid (*(cj->left)) == typeid (IRNEseq))) {
        this->changed = true;
        IRNEseq* eseqAux = (IRNEseq*) cj->left;
        IRNCJump* cjumpAux = new IRNCJump(cj->relOp, eseqAux->exp, cj->right, cj->iftrue, cj->iffalse);
        return (IRNStm*) (new IRNSeq(eseqAux->stm, (IRNStm*) cjumpAux));
    }

    if (cj->right && (typeid (*cj->right) == typeid (IRNEseq))) {
        this->changed = true;
        IRNEseq* eseqAux = (IRNEseq*) cj->right;
        Temp* t = new Temp();
        IRNCJump* cjumpAux = new IRNCJump(cj->relOp, (IRNExp*) (new IRNTemp(t)), eseqAux->exp, cj->iftrue, cj->iffalse);
        IRNMove* moveAux = new IRNMove((IRNExp*) (new IRNTemp(t)), cj->left);
        return (IRNStm*) (new IRNSeq((IRNStm*) moveAux, (IRNStm*) (new IRNSeq(eseqAux->stm, (IRNStm*) cjumpAux))));
    }
    return (IRNStm*) cj;
}

IRNStm* VisitorIRTCanonizer::visit(IRNSeq *s) {
    if (s->left) s->left = s->left->acceptVisitor(this);
    if (s->right) s->right = s->right->acceptVisitor(this);

    if (s->right && (typeid (*s->right) != typeid (IRNSeq))) {
        this->changed = true;
        s->right = new IRNSeq((IRNStm*) s->right, NULL);
    }

    if (s->left && (typeid (*s->left) == typeid (IRNSeq))) {
        this->changed = true;
        IRNSeq* fseq = (IRNSeq*) s->left;
        IRNSeq* seq = new IRNSeq((IRNStm*) fseq->right, (IRNStm*) s->right);
        s->left = (IRNStm*) fseq->left;
        s->right = (IRNStm*) seq;
    }

    if (s->left == NULL && s->right == NULL) {
        this->changed = true;
        return NULL;
    }

    if (s->left == NULL && s->right != NULL) {
        this->changed = true;
        s->left = (IRNStm*) s->right;
        s->right = NULL;
    }

    return (IRNStm*) s;
}

IRNStm* VisitorIRTCanonizer::visit(IRNLabel *l) {
    if (l->label) l->label->acceptVisitor(this);
    return (IRNStm*) l;
}

/*********************************************************************
 *         Implementação da Classe VisitorIRTAsmTranslator           *
 *********************************************************************/
VisitorIRTAsmTranslator::VisitorIRTAsmTranslator() {
    this->frameSize = 0;
    this->isLiteral = false;
    this->availableTemps.push_back(new Temp((char*) "$t0"));
    this->availableTemps.push_back(new Temp((char*) "$t1"));
    this->availableTemps.push_back(new Temp((char*) "$t2"));
    this->availableTemps.push_back(new Temp((char*) "$t3"));
    this->availableTemps.push_back(new Temp((char*) "$t4"));
    this->availableTemps.push_back(new Temp((char*) "$t5"));
    this->availableTemps.push_back(new Temp((char*) "$t6"));
    this->availableTemps.push_back(new Temp((char*) "$t7"));
}

VisitorIRTAsmTranslator::~VisitorIRTAsmTranslator() {
}

int VisitorIRTAsmTranslator::visit(StmtFragmentList* sfl) {
    if (sfl->stmFrag) sfl->stmFrag->acceptVisitor(this);
    if (sfl->nextStmFrag) sfl->nextStmFrag->acceptVisitor(this);
    return 0;
}

int VisitorIRTAsmTranslator::visit(Access_List *al) {
    return 0;
}

int VisitorIRTAsmTranslator::visit(Label *l) {
    char aux[256];
    if (this->isLiteral) {
        Label *label = new Label();
        sprintf(aux, "%s :", label->Label_Name);
        this->asmCode.push_back(aux);
        return (int) label;
    } else {
        sprintf(aux, "%s :", l->Label_Name);
        this->asmCode.push_back(aux);
        return 0;
    }
}

int VisitorIRTAsmTranslator::visit(Temp *t) {
    return 0;
}

int VisitorIRTAsmTranslator::visit(Label_List *ll) {
    return 0;
}

int VisitorIRTAsmTranslator::visit(Temp_List *tl) {
    return 0;
}

int VisitorIRTAsmTranslator::visit(FragmentList *fl) {
    int offSetMaxL = 0;
    int offSetMaxP = 0;
    Method *meth = NULL;
    FrameMIPS *frame = NULL;
    FragmentList *aux = fl;
    while (aux) {
        if (typeid (*aux->frag) == typeid (Method)) {
            meth = (Method*) aux->frag;
            frame = (FrameMIPS*) meth->frame;
            if (frame->offSetAmount > offSetMaxL) offSetMaxL = frame->offSetAmount;
            if (frame->offSetParamAmount > offSetMaxP) offSetMaxP = frame->offSetParamAmount;
        }
        aux = aux->nextFrag;
    }
    this->frameSize = offSetMaxP + offSetMaxL;
    if (fl->frag) fl->frag->acceptVisitor(this);
    if (fl->nextFrag) fl->nextFrag->acceptVisitor(this);
    return 0;
}

int VisitorIRTAsmTranslator::visit(Method *m) {
    char aux[256];
    FrameMIPS *frame = (FrameMIPS*) m->frame;
    this->asmCode.push_back(".text");
    sprintf(aux, ".globl %s", frame->label->Label_Name);
    this->asmCode.push_back(aux);
    frame->label->acceptVisitor(this);
    sprintf(aux, "subu $sp,$sp,%d", this->frameSize);
    this->asmCode.push_back(aux);
    sprintf(aux, "sw $ra,%d($sp)", this->frameSize);
    this->asmCode.push_back(aux);
    sprintf(aux, "sw $fp,%d($sp)", this->frameSize-4);
    this->asmCode.push_back(aux);
    sprintf(aux, "addi $fp,$sp,%d", this->frameSize-4);
    this->asmCode.push_back(aux);

    if(m->body) m->body->acceptVisitor(this);

    sprintf(aux, "lw $ra,%d($sp)", this->frameSize);
    this->asmCode.push_back(aux);
    sprintf(aux, "lw $fp,%d($sp)", this->frameSize-4);
    this->asmCode.push_back(aux);
    sprintf(aux, "addi $sp,$sp,%d", this->frameSize);
    this->asmCode.push_back(aux);
    this->asmCode.push_back("jr $ra");
    return 0;
}

int VisitorIRTAsmTranslator::visit(Literal_Frag *lf) {
    return 0;
}

int VisitorIRTAsmTranslator::visit(InFrame *i) {
    return 0;
}

int VisitorIRTAsmTranslator::visit(InReg *i) {
    return 0;
}

int VisitorIRTAsmTranslator::visit(FrameMIPS *f) {
    return 0;
}

int VisitorIRTAsmTranslator::visit(IRNStmList *sl) {
    if (sl->stm) sl->stm->acceptVisitor(this);
    if (sl->tail) sl->tail->acceptVisitor(this);
    return 0;
}

int VisitorIRTAsmTranslator::visit(IRNExpList *el) {
    return 0;
}

int VisitorIRTAsmTranslator::visit(IRNConst *c) {
    return 0;
}

int VisitorIRTAsmTranslator::visit(IRNConstf *c) {
    return 0;
}

int VisitorIRTAsmTranslator::visit(IRNName *n) {
    return 0;
}

int VisitorIRTAsmTranslator::visit(IRNTemp *t) {
    return 0;
}

int VisitorIRTAsmTranslator::visit(IRNBinop *b) {
    return 0;
}

int VisitorIRTAsmTranslator::visit(IRNMem *m) {
    return 0;
}

int VisitorIRTAsmTranslator::visit(IRNCall *el) {
    char aux[256];
    if (typeid(*el->func) == typeid(IRNName)) {
        IRNName *name = (IRNName*) el->func;
        if (strcmp(name->label->Label_Name, (char*)"SYSTEM_CALL_PRINTLN") == 0) {
            IRNLabel *label = (IRNLabel*) el->args->head;
            this->isLiteral = true;
            Label *lb = (Label*)label->acceptVisitor(this);
            this->isLiteral = false;
            this->asmCode.push_back(".text");
            this->asmCode.push_back("li $v0,0x04");
            sprintf(aux, "la $a0,%s", lb->Label_Name);
            this->asmCode.push_back(aux);
            this->asmCode.push_back("syscall");
        }
    }
    return 0;
}

int VisitorIRTAsmTranslator::visit(IRNEseq *c) {
    return 0;
}

int VisitorIRTAsmTranslator::visit(IRNMove *n) {
    return 0;
}

int VisitorIRTAsmTranslator::visit(IRNExpStm *es) {
    es->exp->acceptVisitor(this);
    return 0;
}

int VisitorIRTAsmTranslator::visit(IRNJump *j) {
    return 0;
}

int VisitorIRTAsmTranslator::visit(IRNCJump *cj) {
    return 0;
}

int VisitorIRTAsmTranslator::visit(IRNSeq *s) {
    if (s->left) s->left->acceptVisitor(this);
    if (s->right) s->right->acceptVisitor(this);
    return 0;
}

int VisitorIRTAsmTranslator::visit(IRNLabel *l) {
    char aux[256];
    Label *lb = NULL;
    this->asmCode.push_back(".rdata");
    lb = (Label*) l->label->acceptVisitor(this);
    sprintf(aux, ".asciiz %s", l->label->Label_Name);
    this->asmCode.push_back(aux);
    return (int) lb;
}